【问题标题】:Delphi, MSXML: how to retrieve node XML without the document namespace?Delphi,MSXML:如何在没有文档命名空间的情况下检索节点 XML?
【发布时间】:2011-02-10 16:57:38
【问题描述】:

我需要对 XML 文档进行一些解析和信息检索。 XML 文档绑定到 XML 数据绑定,然后针对特定元素进行解析。 一旦我隔离了我需要剖析的元素,我会依次取出每个元素(我们称之为 E_parent)并尝试识别每个非文本子元素(E_child)在 E_parent 的整个 XML 文本中的位置并进行一些操作或其他。

我遇到的问题是,当单独访问子元素时,XML 文档的命名空间被添加到了子元素的 XML 中。

举个例子,假设原始文档如下所示:

<?xml version="1.0" encoding="windows-1252"?>
<RootNode xml:lang="en" xmlns="urn:blah:names:blahblah">
<E_parent>Some text <E_child>child text</E_child> more parent text</E_parent>
</RootNode>
</xml>

当我尝试通过执行以下操作从 E_parent 或 E_child 元素访问 XML 时:

xmlParent := parentNode.XML;

我明白了:

<E_parent xmlns="urn:blah:names:blahblah">Some text <E_child>child text</E_child> more parent text</E_parent>

如果我尝试访问 E_child 的 XML,我会得到:

<E_child xmlns="urn:blah:names:blahblah">child text</E_child>

当我尝试对父元素进行文本搜索时,这是一个问题,因为“真实”文本不包含该命名空间声明:

Some text <E_child>child text</E_child> more parent text

到目前为止,我已经通过在字符串中查找/删除不需要的命名空间属性来解决这个问题,但它的效率非常低,而且有点难看;o) 所以,我的问题是,如何在不将文档命名空间添加到标签的情况下从绑定的 XML 文档中检索各个节点的 XML?

=========

感谢 Remy,这很明显,我只需要从一个空白字符串开始构建它,而不是从内部 XML 开始!

请注意,这比我在这种特定情况下的解决方法更好,但不是我想要的 - 获取没有命名空间的元素的 XML 仍然对其他事情有用,例如日志记录,其中我想要在原始文档中出现的节点的确切 XML。

【问题讨论】:

  • XML 中的文本搜索?您真正想在这里完成什么?
  • @Leonardo:我应该发布答案之前问的问题。
  • 如果您想记录确切的 XML,那么您根本不应该从您的 XML 对象中提取任何内容。这样做会 重新序列化 对象,因此即使它不包含名称空间,它仍可能与原始 XML 文本不同。相反,请返回并首先记录您放入 XML 对象的原始文本。
  • 如果我只是浏览一个发送给我的 XML 文档,我就没有原件,我需要以某种方式得到它。我只是通过我的绑定类加载 XML 并访问该元素,寻找其确切的外部 XML。这是我的全部问题,现在看来我唯一的选择是从中获取元素,获取其内部 XML 并手动解析,或者直接获取元素的 XML 并删除 namspace 属性。无论哪种方式,都必须进行一些“手动”字符串操作

标签: xml delphi namespaces msxml


【解决方案1】:

使用 DOM 来处理 E_parent 的内容。而不是检索 E_parent 的 XML 然后在其中搜索 E_child 标记,而是使用 DOM 确定 E_child 节点前面存在哪些纯文本(纯文本将有自己的子节点),以及长度该纯文本将告诉您 E_Child 的确切文本位置,而根本不需要检索 E_parent 的XML。 E-parent 将在每个未标记文本部分的相关位置有多个纯文本子节点。

换句话说,给定您展示的 XML,DOM 的结构将如下所示:

RootNode
|
-- E_parent
   |
   |- "Some text "
   |
   |- E_child
   |  |
   |  -- "child text"
   |
   -- " more parent text"

【讨论】:

  • 确实如此,而且我没想到这么简单。我只需要从一个空白字符串开始,然后遍历子元素,如果它们是文本,则将它们添加到结果字符串中,否则标记它们的位置(以及我需要的任何其他信息),然后继续。谢谢!
【解决方案2】:

另一种方法是使用 XPath 导航您的 xml。

给定示例 XML

<?xml version="1.0" encoding="windows-1252"?>
<RootNode xml:lang="en" xmlns="urn:blah:names:blahblah">
<E_parent>Some text <E_child>child text</E_child> more parent text</E_parent>
</RootNode>

您可以使用 MSXML 解析器通过一点 XPath 直接导航到您的 E_child 元素。首先,您需要制作自己的 MSXML2_TLB 单元副本。您可以使用看起来像这样的 Delphi 代码来访问 E_child 节点:

uses MSXMLDOM,MSXML2_TLB;

procedure Sample;
var
  doc: IXMLDOMDocument2;
  root: IXMLDomElement;
  nodes: IXMLDOMNodeList;
  node: IXMLDOMNode;
begin

  doc := CoDOMDocument60.Create;
  doc.async := false;
  // Use same namespace as the default namespace here
  doc.setProperty('SelectionNamespaces', 'xmlns:t="urn:blah:names:blahblah"');
  doc.setProperty('SelectionLanguage', 'XPath');
  doc.loadXML(XmlSource.Text);

  root := doc.documentElement;
  nodes := root.selectNodes('//t:E_child');

  // Now thee nodes contains all E_child nodes
  // Processs them here
  // ...
end;

关键是您为 XPath 查询的文档默认命名空间使用特定前缀。 //t:E_child 是用于查找 E_child 元素的实际 XPath 表达式。

【讨论】:

    【解决方案3】:

    使用您拥有的代码,然后使用 Pos/PoxEx 查找 E_Child 元素的开始和结束。

    var
      cStart, cEnd: Integer;
      ChildName, ChildText: string;
    begin
      ... other code
      xmlParent := parentNode.XML;
      ChildName := 'E_Child';
      // Find starting position of child tag
      cStart := Pos('<' + E_Child, xmlParent);
      // You now have the opening <
      cEnd   := PosEx('</' + E_Child, xmlParent, cStart);
      // You now have the final < of the child.
      // Add the length of the child's name + the closing >
      Inc(cEnd, Length('</' + E_Child + '>'));
      // Grab the entire child XML
      ChildText := System.Copy(xmlParent, cStart, cEnd - cStart);
      // Do whatever you want with the child. For instance,
      // remove the original text.
      System.Delete(xmlParent, cStart, cEnd - cStart);
      // Replace it with new text
      System.Insert(NewChildText, xmlParent, cStart);
    end;
    

    【讨论】:

    • 我想过这样做,但是因为相关的子元素有几种类型(现在〜12个,但可能会增长),我认为循环通过ChildNodes会更快E_parent,检查它是否是一个元素(不是文本节点),然后才检查它是否是我感兴趣的类型(如果是则替换它的 XML)。它应该比检查每个节点的开始标签的每个变体更快。正如我所提到的,我找到了一种解决方法,但我希望有一个更“优雅”的解决方案。
    • 那么你的问题没有意义。如果您计划循环通过 E_Parent 的子节点,则不必担心文档命名空间。只需遍历 E_Parent.ChildNodes 即可,命名空间无关紧要。我以为您正在尝试进行迭代,而是出于某种原因使用原​​始 XML。
    • 很多。 :) 谢谢。雷米的回答比我的要好得多;不过,我会留下我的,以防有一天它对某人有好处。
    【解决方案4】:

    基本上,您只能使用 XML 解析器来解析 XML。 RegEx won't work。任何比 RegEx 更简单的东西都行不通。

    在某些时候,您尝试解析的 XML 会发生变化,从而破坏您的简单搜索/替换代码。

    您需要做的是用 XML 术语定义应该用哪个替换,而不是用文本术语。

    您将最终定义应该更改/插入/删除哪些节点。

    然后你需要把它翻译成 Delphi DOM 代码。

    可以提供很大帮助的是一个 XML 工具(如 XML Spy,但还有更多),它可以为您提供 XML 的 DOM 树视图。

    将原来的旧 XML 和更改后的新 XML 并排放置。

    从那里,您可以直观地看到新旧树,这会引导您写下所需的 XML 节点的更改。

    --杰罗恩

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-03
      • 1970-01-01
      • 2017-12-11
      • 2011-02-18
      • 1970-01-01
      • 1970-01-01
      • 2012-02-10
      • 1970-01-01
      相关资源
      最近更新 更多