html如何转换为opml,使用并解析 OPML 格式的订阅列表来转移自己的 RSS 订阅(解析篇)...

本文介绍了如何理解OPML格式,提供了.NET上的OPML解析器实现,包括创建OPML模型和解析OPML文档的详细步骤。通过XPath语法,可以将HTML格式的RSS源转换为OPML,方便用户转移订阅。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

OPML 全称是 Outline Processor Markup Language ,即 大纲处理标记语言 。目前流行于收集博客的 RSS 源,便于用户转移自己的订阅项目。

本文将介绍这个古老的格式,并提供一个 .NET 上的简易解析器。

本文是两个部分的第二篇,前者是理解 OPML 格式,此篇是解析此格式:

OPML 格式

在解析之前,最好先理解此格式的的元素组成和元素属性,所以如果你没有阅读概念篇,请先前往阅读。

创建适用于 RSS 的简易 OPML 模型

我们先为模型创建基类 OpmlModel 。

为了方便在客户端应用中使用,可以使其继承自 INotifyPropertyChanged 。

namespace Walterlv.Rssman.Models

{

public abstract class OpmlModel : NotificationObject

{

public void Deserialize(XElement element)

{

OnDeserializing(element);

}

protected abstract void OnDeserializing(XElement element);

}

}

namespace Walterlv.Rssman.Models

{

public abstract class NotificationObject : INotifyPropertyChanged

{

public event PropertyChangedEventHandler PropertyChanged;

[NotifyPropertyChangedInvocator]

protected void SetValue(ref T field, T value, [CallerMemberName] string propertyName = null)

{

if (Equals(field, value)) return;

field = value;

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

}

[NotifyPropertyChangedInvocator]

protected void NotifyPropertyChanged([CallerMemberName] string propertyName = null)

{

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

}

}

}

拿出我们关心的 outline 的属性来解析,于是有:

namespace Walterlv.Rssman.Models

{

[DebuggerDisplay("RssOutline {Text,nq}, {XmlUrl,nq}, Count={Children.Count,nq}")]

public sealed class RssOutline : OpmlModel

{

private string _text;

private OutlineType _type;

private string _xmlUrl;

private string _htmlUrl;

public string Text

{

get => _text;

set => SetValue(ref _text, value);

}

public OutlineType Type

{

get => _type;

set => SetValue(ref _type, value);

}

public string XmlUrl

{

get => _xmlUrl;

set => SetValue(ref _xmlUrl, value);

}

public string HtmlUrl

{

get => _htmlUrl;

set => SetValue(ref _htmlUrl, value);

}

public bool HasChildren => Children.Any();

public ObservableCollection Children { get; } = new ObservableCollection();

protected override void OnDeserializing(XElement element)

{

// 等待编写解析代码。

}

}

}

还有表示 OPML 文档的模型:

namespace Walterlv.Rssman.Models

{

[DebuggerDisplay("RssOpml {Title,nq}, Count={Children.Count,nq}")]

public sealed class RssOpml : OpmlModel

{

private string _title;

public string Title

{

get => _title;

set => SetValue(ref _title, value);

}

public ObservableCollection Children { get; } = new ObservableCollection();

protected override void OnDeserializing(XElement element)

{

// 等待编写解析代码。

}

}

}

从 OPML 文档中解析出模型

在以上的模型代码中,我为基类留有 OnDeserializing 方法以供反序列化。

为了尽可能简化此博客的代码,参数我直接使用了 XElement 类型,以便在方法中使用 XPath 语法来解析。(当然,如果你是做库或者进行大型可维护项目的开发,这里就需要一些抽象了。)

现在,我们写一个新的静态类型 Opml 来解析 OPML 文档:

namespace Walterlv.Rssman.Services

{

public static class Opml

{

public static async Task ParseAsync(Stream stream)

{

var document = await XDocument.LoadAsync(stream, LoadOptions.None, CancellationToken.None);

var root = document.XPathSelectElement("opml");

var opml = new RssOpml();

opml.Deserialize(root);

return opml;

}

}

}

于是,再补全模型 RssOpml 和 RssOutline 的反序列化部分:

// RssOpml.cs

protected override void OnDeserializing(XElement element)

{

var title = element.XPathSelectElement("head/title");

Title = title?.Value;

var outlines = element.XPathSelectElements("body/outline");

Children.Clear();

foreach (var value in outlines)

{

var outline = new RssOutline();

outline.Deserialize(value);

Children.Add(outline);

}

}

// RssOutline.cs

protected override void OnDeserializing(XElement element)

{

var text = element.Attribute("text");

Text = text?.Value;

var type = element.Attribute("type");

if (type != null && Enum.TryParse(type.Value, out OutlineType outlineType))

{

Type = outlineType;

}

var xmlUrl = element.Attribute("xmlUrl");

XmlUrl = xmlUrl?.Value;

var htmlUrl = element.Attribute("htmlUrl");

HtmlUrl = htmlUrl?.Value;

var outlines = element.XPathSelectElements("outline");

Children.Clear();

foreach (var value in outlines)

{

var outline = new RssOutline();

outline.Deserialize(value);

Children.Add(outline);

}

}

注意,以上两个方法请分别填充到 RssOpml.cs 和 RssOutline.cs 的 OnDeserializing 方法中。

这里,所有的 XML 解析均使用的是 XPath 语法,关于 XPath 语法,可以阅读 XML 的 XPath 语法 - walterlv ,关于如何使用 XPath 在 .NET 中读写 XML 文件,可以阅读 .NET 使用 XPath 来读写 XML 文件 - walterlv 。

使用此 OPML 模型

当你把这些类都准备好,那么你就可以使用简单的几句话来完成 OPML 文档的解析了。

在 UWP 应用中,可以通过 StorageFile 来打开一个文件流:

var folder = Package.Current.InstalledLocation;

using (var stream = await folder.OpenStreamForReadAsync("sample-opml.xml"))

{

var opml = await Opml.ParseAsync(stream);

// 使用此 OPML 文档

}

在 .NET Framework 传统应用中,可以使用 File.Read 来打开一个文件流。

由于我们本文中创建的模型均实现了 INotifyPropertyChanged 接口,所以你甚至可以直接将 Opml.ParseAsync 的返回结果应用于绑定。

3ca69510600ff1de6d66488decd65691.png 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接:https://walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 (walter.lv@qq.com) 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值