Delphi,MSXML:如何在没有文档命名空间的情况下检索节点XML?

时间:2011-02-10 16:57:38

标签: xml delphi namespaces msxml

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

我遇到的问题是,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。

4 个答案:

答案 0 :(得分: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"

答案 1 :(得分:1)

另一种方法是使用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表达式。

答案 2 :(得分:0)

使用您拥有的代码,然后使用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;

答案 3 :(得分:0)

基本上,除了XML解析器之外,您不能使用任何东西来解析XML。 RegEx won't work。任何比RegEx简单的东西都不会起作用。

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

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

您将最终定义应更改/插入/删除节点的内容。

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

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

放置原始旧XML并将新XML更改为彼此。

从那里,您可以直观地看到旧树和新树,这将导致您记下所需的XML节点中的更改。

- 的Jeroen