当前位置: 首页 > news >正文

使用并解析 OPML 格式的订阅列表来转移自己的 RSS 订阅(概念篇)

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

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


本文分为两个部分,一个是理解 OPML 格式,一个是解析此格式:

  • 概念篇(本文)
  • 解析篇

本文内容

    • OPML 格式
    • 典型的 OPML 文件
    • OPML 文件中的节点解释
      • opml 根节点
      • head 节点
      • body 节点
      • outline(普通)
      • outline(RSS 专属)
    • OPML 的解析

OPML 格式

RSS 订阅你应该并不陌生,你可以在我的博客上方看到 RSS 的订阅源按钮,也可以在各大博客站点发现这样的订阅按钮。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oax9S9Ht-1582957371395)(/static/posts/2018-09-23-feed-icon.svg)]
▲ RSS 图标

图片来源于维基百科,如果你不太了解 RSS,可以直接前往 RSS - 维基百科,自由的百科全书 查看或者自己搜索。

OPML 是个古老的格式,第一个版本还是二十世纪六十年代的产物呢(详见 OPML 1.0 Specification);只不过实际在用的 1.0 版本是 2000 年发布的,2.0 版本是 2007 年发布的。这么古老的格式也不妨碍它依然成为订阅源交换的标准格式。不过我们这篇文章不会去谈历史,我们只谈它的格式以及使用。

OPML 官网对其作用的描述为:

The purpose of this format is to provide a way to exchange information between outliners and Internet services that can be browsed or controlled through an outliner.

OPML is also the file format for an outliner application, which is why OPML files may contain information about the size, position and expansion state of the window the outline is displayed in.

OPML has also become popular as a format for exchanging subscription lists between feed readers and aggregators.

其中最后一行的描述即交换订阅,尤其是 RSS 订阅。

典型的 OPML 文件

为了直观地了解 OPML 格式,我直接贴一个我的订阅的极简版文件内容。

<?xml version="1.0" encoding="UTF-8"?>
<opml version="1.0">
  <head>
    <title>walterlv</title>
  </head>
  <body>
    <outline text="walterlv" title="walterlv" type="rss" xmlUrl="https://blog.walterlv.com/feed.xml" htmlUrl="https://blog.walterlv.com/" />

    <outline title="Team" text="Team">
      <outline text="林德熙" title="林德熙" type="rss" xmlUrl="https://blog.lindexi.com/feed.xml" htmlUrl="https://blog.lindexi.com/" />
    </outline>

    <outline title="Microsoft" text="Microsoft">
      <outline text="Microsoft .NET Blog" title="Microsoft .NET Blog" type="rss" xmlUrl="https://blogs.msdn.microsoft.com/dotnet/feed/"/>
      <outline text="Microsoft The Visual Studio Blog" title="Microsoft The Visual Studio Blog" type="rss" xmlUrl="https://blogs.msdn.microsoft.com/visualstudio/feed/"/>
    </outline>
  </body>
</opml>

你可以很容易地看出它的一些特征。比如以 opml 为根,head 中包含 titlebody 中包含分组的 outline。每一个 outline 中包含 text, type, xmlUrl 等属性。接下来我们详细描述这个格式。

OPML 文件中的节点解释

opml 根节点

<opml> 是 OPML 格式文件的根节点,其 version 属性是必要的。它的值可能为 1.02.0;如果是 1.0,则视为符合 OPML 1.0 规范;如果是 2.0,则视为符合 OPML 2.0 规范。额外的,值也可能是 1.1,那么也视为符合 1.0 规范。

opml 根节点中包含 headbody 节点。

head 节点

head 节点可包含 0 个或多个元素:

  • title
    • 这就是 OPML 文档标题
  • dateCreated
    • 文档创建时间
  • dateModified
    • 文档修改时间
  • ownerName
    • 文档作者
  • ownerEmail
    • 文档作者的邮箱
  • ownerId
    • 文档作者的 url,要求不存在相同 Id 的两个作者
  • docs
    • 描述此文档的文档的 url

当然,这些都是可选的。

额外的,还有 expansionState, vertScrollState, windowTop, windowLeft, windowBottom, windowRight

body 节点

body 节点包含一个或多个 outline 元素。

outline(普通)

outline 元素组成一个树状结构。也就是说,如果我们使用 OPML 储存 RSS 订阅列表,那么可以存为树状结构。在前面的例子中,我把自己的 RSS 订阅独立开来,把朋友和微软的 RSS 订阅分成了单独的组。

outline 必须有 text 属性,其他都是可选的。而 text 属性就是 RSS 订阅的显示文字,如果没有这个属性,那么 RSS 的订阅列表中将会是空白一片。

于是,我们解析 text 属性便可以得到可以显示出来的 RSS 订阅列表。对于前面的例子对应的 RSS 订阅列表就可以显示成下面这样:

- walterlv
- Team
    - 林德熙
- Microsoft
    - Microsoft .NET Blog
    - Microsoft The Visual Studio Blog

outline 还有其他可选属性:

  • type
    • 指示此 outline 节点应该如何解析
  • isComment
    • 布尔值,为 truefalse;如果为 true,那么次 outline 就只是注释而已
  • isBreakpoint
    • 适用于脚本,执行时可下断点
  • created
    • 一个时间,表示此节点的创建时间
  • category
    • 逗号分隔的类别:如果表示分类,则要用 / 分隔子类别;如果表示标签,则不加 /
    • 例如:/Boston/Weather, /Harvard/Berkman,/Politics(例子来源于官方规范)

outline(RSS 专属)

typerss 时,还有一些 RSS 专属属性。这时,必要属性就有三个了:

  • type
  • text
  • xmlUrl

其中,xmlUrl 就指的是订阅源的 url 地址了。在官方规范中,规定解析器不应该总认为 text 存在,相比之下,xmlUrl 显得更加重要。

还有一些可选属性:

  • description
  • htmlUrl
  • language
  • title
  • version

OPML 的解析

在了解了 OPML 的格式组成之后,便可以很容易的地解析此文件了。当然,我也写了一份 OPML 的解析,请参阅本文的第二部分,解析篇。


我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

知识共享许可协议

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

相关文章:

  • csproj 文件中那个空的 NuGetPackageImportStamp 是干什么的?
  • C#/.NET 中 Thread.Sleep(0), Task.Delay(0), Thread.Yield(), Task.Yield() 不同的执行效果和用法建议
  • WPF 中那些可跨线程访问的 DispatcherObject(WPF Free Threaded Dispatcher Object)
  • 在 Visual Studio Code 中为代码片段(Code Snippets)添加快捷键
  • 在 Visual Studio 中使用 EditorConfig 统一代码风格(含原生与插件)
  • 在 Visual Studio Code 中添加自定义的代码片段
  • 用 dotTrace 进行性能分析时,Timeline 打不开?无法启动进程?也许你需要先开启系统性能计数器的访问权限
  • 了解 .NET/C# 程序集的加载时机,以便优化程序启动性能
  • git 如何更可靠地解决冲突?
  • .NET 编写一个可以异步等待循环中任何一个部分的 Awaiter
  • 文件被占用?系统自带的“资源监视器(resmon)”也能帮你找到占用它的真凶
  • Windows 系统文件资源管理器的命令行参数(如何降权打开程序,如何选择文件)
  • 为 .NET 各种开发工具设置网络代理,提升在大陆的网络性能
  • 如何在旧版本的 .NET Core / Framework 中使用 C# 8 的异步流(IAsyncDisposable / IAsyncEnumerable / IAsyncEnumerator)
  • .NET/C# 解压 Zip 文件时出现异常:System.IO.InvalidDataException: 找不到中央目录结尾记录。
  • [nginx文档翻译系列] 控制nginx
  • ComponentOne 2017 V2版本正式发布
  • exif信息对照
  • iOS编译提示和导航提示
  • Mybatis初体验
  • React系列之 Redux 架构模式
  • SQLServer之索引简介
  • 第2章 网络文档
  • 工程优化暨babel升级小记
  • 前端学习笔记之观察者模式
  • 如何用vue打造一个移动端音乐播放器
  • 使用iElevator.js模拟segmentfault的文章标题导航
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 字符串匹配基础上
  • raise 与 raise ... from 的区别
  • zabbix3.2监控linux磁盘IO
  • 阿里云API、SDK和CLI应用实践方案
  • # 数据结构
  • #define
  • #include<初见C语言之指针(5)>
  • (2021|NIPS,扩散,无条件分数估计,条件分数估计)无分类器引导扩散
  • (附源码)springboot宠物医疗服务网站 毕业设计688413
  • (五)Python 垃圾回收机制
  • (小白学Java)Java简介和基本配置
  • (学习日记)2024.01.09
  • (转载)在C#用WM_COPYDATA消息来实现两个进程之间传递数据
  • ***详解账号泄露:全球约1亿用户已泄露
  • ... fatal error LINK1120:1个无法解析的外部命令 的解决办法
  • .NET core 自定义过滤器 Filter 实现webapi RestFul 统一接口数据返回格式
  • .NET Core实战项目之CMS 第十二章 开发篇-Dapper封装CURD及仓储代码生成器实现
  • .net framework 4.0中如何 输出 form 的name属性。
  • .Net的DataSet直接与SQL2005交互
  • .NET牛人应该知道些什么(2):中级.NET开发人员
  • ::before和::after 常见的用法
  • ??javascript里的变量问题
  • @cacheable 是否缓存成功_Spring Cache缓存注解
  • @SuppressWarnings注解
  • [ 云计算 | AWS ] AI 编程助手新势力 Amazon CodeWhisperer:优势功能及实用技巧
  • [202209]mysql8.0 双主集群搭建 亲测可用
  • [Android] Upload package to device fails #2720