XML DOM API

本文适用于创建自定义 Web 服务器集成或一些 UI 以轻松编辑 XML 的插件作者。

它描述了 IntelliJ 平台中的文档对象模型 (DOM) -

一种使用 DTD 或基于模式的 XML 模型的简单方法。 将涵盖以下主题:使用 DOM 本身(读/写标签内容、属性和子标签)以及通过将 UI 连接到 DOM 在 UI 中轻松编辑 XML。

假定读者熟悉 Java、Swing、IntelliJ 平台 XML PSI(类 XmlTag、XmlFile、XmlTagValue 等)、IntelliJ 平台插件开发基础知识(应用程序和项目组件、文件编辑器)。

XML PSI 与 DOM

那么,如何使用 IntelliJ 平台插件中的 XML 进行操作呢?

通常,必须获取 XmlFile,获取其根标签,然后通过路径找到所需的子标签。

该路径由标签名称组成,每个标签名称都是一个字符串。

到处输入这些内容既乏味又容易出错。 假设您有以下 XML:

<root>
  <foo>
    <bar>42</bar>
    <bar>239</bar>
  </foo>
</root>

假设您要读取第二个 bar 元素的内容,即“239”。

创建链式调用是不正确的

file.getDocument()
    .getRootTag()
    .findFirstSubTag("foo")
    .findSubTags("bar")[1]
    .getValue()
    .getTrimmedText();

因为这里每次调用都可能返回null。

所以代码可能看起来像这样:

XmlFile file = ...;
XmlDocument document = file.getDocument();
if (document != null) {
  XmlTag rootTag = document.getRootTag();
  if (rootTag != null) {
    XmlTag foo = rootTag.findFirstSubTag("foo");
    if (foo != null) {
      XmlTag[] bars = foo.findSubTags("bar");
      if (bars.length > 1) {
        String s = bars[1].getValue().getTrimmedText();
        // do something
      }
    }
  }
}

看起来很糟糕,不是吗? 但是有更好的方法来做同样的事情。

你只需要扩展一个特殊的接口——DomElement。

例如,让我们创建几个接口:

interface Root extends com.intellij.util.xml.DomElement {
  Foo getFoo();
}

interface Foo extends com.intellij.util.xml.DomElement {
  List<Bar> getBars();
}

interface Bar extends com.intellij.util.xml.DomElement {
  String getValue();
}

接下来,您应该创建一个 DomFileDescription 类,将根标签名称和根元素接口传递给它的构造函数。

使用 com.intellij.dom.fileMetaData 扩展点在 plugin.xml 中注册它并指定 rootTagName 和 domVersion/stubVersion 属性。

您现在可以从 DomManager 获取文件元素。 要获取“239”值,只需编写以下代码:

DomManager manager = DomManager.getDomManager(project);
Root root = manager.getFileElement(file).getRootElement();
List<Bar> bars = root.getFoo().getBars();
if (bars.size() > 1) {
  String s = bars.get(1).getValue();
  // do something
}

我想这看起来好一点。 您经常在多个地方使用您的模型。

重新创建模型效率太低,因此我们为您缓存它,并且对 DomManager.getFileElement() 的任何后续调用都将返回相同的实例。

因此,只调用此方法一次,然后在所有地方只保留您获得的“根”对象是很有用的。

在这种情况下,您不需要重复那可怕的第一行,而且代码看起来会更好。

同样重要的是要注意,在这种情况下,我们避免了潜在的 NullPointerException:我们的 DOM 保证访问标签子标签的每个方法都将返回一个非空元素,即使相应命名的子标签不存在。

乍一看这似乎很奇怪,但它似乎相当方便。 它是如何工作的? 简单的。 给定这些接口,DOM 生成所有代码以在运行时访问正确的子标签和创建模型元素。

子标签名称和元素类型取自方法名称、返回类型和方法注释(如果有)。 在大多数情况下,可以省略注释,就像在我们的示例中一样,但无论如何本文都会对此进行进一步讨论。

现在让我们更彻底地探索 DOM 可以做什么,并查看表示各种 XML 概念(例如标记内容、属性或子标记)的可能方式。

稍后,我们将讨论使用模型的基本方法,以及更高级的功能。 最后,我们将了解如何轻松地为 DOM 模型元素创建 UI 编辑器。

参考资料

https://plugins.jetbrains.com/docs/intellij/xml-dom-api.html