【问题标题】:xpath return string instead of nodelistxpath 返回字符串而不是节点列表
【发布时间】:2011-05-13 21:52:47
【问题描述】:

我正在做一个 biztalk 项目,我需要将(过滤的)内容从 1 个 xml 复制到另一个。 我必须用 xpath 来做这个,我不能使用 xsl 转换。 所以我从源xml文件中获取内容的xpath是这样的:

//*[not(ancestor-or-self::IN1_Insurance)]|//IN1_Insurance[2]/descendant-or-self::*

现在返回一个 xmlNodelist。是否可以返回一个包含所有节点的字符串,例如:

"<root><node>text</node></root>"

如果我将 string() 放在我的 xpath 之前,它会返回值,但我希望将整个 xml 放在一个字符串中(带有节点..),所以我可以将该字符串加载到另一个 xmldocument 中。我认为这是解决我的问题的最佳方法。

我知道我可以遍历 xmlnodelist 并将节点附加到新的 xmldocument,但是在 biztalk 编排中循环有点棘手,我想避免这种情况。

我可以使用的代码是 C#。 我试图将节点列表分配给 xmldocument,但这会引发转换错误(很明显..)。

我看到它的方式是我有 2 个解决方案:

  • 在没有循环的情况下将节点列表分配给 xmldocument(我认为在 C# 中不可能)
  • 以某种方式将节点列表转换为字符串并将其加载到 xmldocument 中
  • 直接在新的xmldocument中加载xpath(不知道这是否可行,因为它返回一个节点列表)

感谢您的帮助

编辑:

样本输入:

<root>
<Patient>
    <PatientId></PatientId>
    <name></name>
</Patient>
<insurance>
    <id>1</id>
    <billing></billing>
</insurance
<insurance>
    <id>2</id>
    <billing></billing>
</insurance>
<insurance>
    <id>3</id>
    <billing></billing>
</insurance>
   </root>

现在我想将此示例复制到另一个 xmldocument,但没有保险节点 2 和 3(这是动态的,因此可能要删除不保险节点 1 和 2,或者 1 和 3...)

所以这必须是输出:

<root>
<Patient>
    <PatientId></PatientId>
    <name></name>
</Patient>
<insurance>
    <id>1</id>
    <billing></billing>
</insurance>
 </root>

我现在正在做的是使用 xpath 来获取我想要的节点。然后我想将结果分配给新的 xmldocument,但这是不可能的,因为我得到了 castException

string xpath = "//*[not(ancestor-or-self::IN1_Insurance)]|//IN1_Insurance[2]/descendant-or-self::*";
xmlDoc = new System.Xml.XmlDocument();
xmlDoc = xpath(sourceXml, strXpath);   <= cast error (cannot cast xmlnodelist to xmldocuemnt)

我知道语法有点奇怪,但它是 biztalk c# 代码..

【问题讨论】:

  • @Dimitre,看起来您删除了 XPath 标记,这是可以理解的,但我不相信这里不存在 XPath 的问题。 OP 使用的 XPath 表达式很有可能是错误的。
  • @LarsH:我编辑了标签,因为 OP 想要的显然不能单独使用 XPath 实现——或者你建议把它放在“not-xpath”下吗?顺便说一句,你检查过你的电子邮件吗?
  • @Dimitre:我认为 xpath 标签适合有两个原因:1)问题“这可以使用 XPath 完成吗?”是一个关于 XPath 的有用问题,即使答案是“否”; 2)即使这个任务不能单独用 XPath 完成(不多),它可以用 XPath 结合环境中的其他工具来完成,在这种情况下,有必要让 XPath 表达式正确。 (是的,我会尽快回复电子邮件。:-)

标签: c# xslt biztalk xpath


【解决方案1】:

最直接的解决方案确实是“循环遍历 xmlnodelist 并将节点附加(导入)到新的 xmldocument”,但是由于您不能循环,那么您可以/不可以做什么其他基本的事情?

要序列化节点列表,您可以尝试使用 XmlNodeList.toString()。如果这有效,您将得到一个奇怪的野兽,因为它可能会多次复制 XML 文档的某些部分。特别是因为您直接在节点列表中明确包括祖先和后代。它不会是您可以重新解析并得到类似于您开始使用的节点列表的结果。

换句话说,最好遍历 XmlNodeList 并将节点导入新的 XmlDocument。

但即便如此,如果你想放置所有这些祖先和后代节点,我会感到非常惊讶:

//*[not(ancestor-or-self::IN1_Insurance)]|//IN1_Insurance[2]/descendant-or-self::

直接进入新的 XML 文档。如果您发布一些示例输入和所需的输出,我们可能会帮助确定是否是这种情况。

更新:

我明白你在做什么:复制一个 XML 文档,省略所有 &lt;insurance&gt; 元素(及其后代),除了你想要的那个。

这可以在没有循环的情况下完成如果输出与您的示例输出一样简单:只有一个&lt;Patient&gt; 和一个&lt;insurance&gt; 元素,以及它们的后代,在一个顶级元素下.

类似的东西(我无法测试这个,因为我没有 biztalk 服务器):

string xpathPatient = "/*/Patient";
string xpathInsuran = "/*/insurance[id = " + insId + "]"; // insId is a parameter
xmlDoc = new System.Xml.XmlDocument();
xmlPatient = xpath(sourceXml, xpathPatient);
xmlInsuran = xpath(sourceXml, xpathInsuran);
XmlElement rootNode  = xmlDoc.CreateElement("root");
xmlDoc.AppendChild(rootNode);
//**Update: use [0] to get an XmlNode from the returned XmlNodeList (presumably)
rootNode.AppendChild(xmlDoc.ImportNode(xmlPatient[0], true));
rootNode.AppendChild(xmlDoc.ImportNode(xmlInsuran[0], true));

我承认,我很好奇为什么不能使用 XSLT。您正在处理在 XSLT 中比在 XPath + C# XmlDocument 中更容易完成的任务。

更新: 因为 xpath() 函数可能返回 XmlNodeList 而不是 XmlNode,所以我在上面 ImportNode() 的第一个参数中添加了 [0]。感谢@Martin Honnen 提醒我这一点。

【讨论】:

  • 我可以使用 System.Xml 库中的所有内容,但我不能使用 for-each 循环 :( 我知道没有循环会很棘手,但我希望有人知道解决方法..
  • @Rise:您拥有 XPath 的方式,您的节点列表包含每个级别的元素。如果您随后对它们进行浅拷贝,它们将全部从原始结构中的位置中删除。如果您制作深层副本,您将复制所有后代。你真的想这样做吗??请发布一些示例输入和所需的输出。
  • 用示例代码编辑了我的问题。或者有没有办法使用 xpath 从原始消息中删除 Isurance 元素?所以我不必将结果复制到另一个 xml 中?
  • @Rise,感谢您提供示例输入/输出。我正在编辑我的答案以回应。
  • @Rise,我添加了一些示例代码。顺便说一句,我对 BizTalk 的了解还不够,无法知道您是否可以从传入的 XML 文档中删除元素并发送修改后的 XML 文档。您不能使用 XPath 删除元素...它只是一种用于从 XML 文档中选择节点的语言。
【解决方案2】:

XPath 是一种(仅)用于 XML 文档的查询语言。

它在一个抽象模型上运行——XML INFOSET,并且不能修改它所运行的 XML 文档的结构或将 INFOSET 信息项序列化回 XML。

因此,实现这种序列化的唯一方法是使用托管 XPath 的语言。

除此之外,你的问题有明显的问题,例如在提供的 XML 文档中没有名为 IN1_Insurance 的元素——因此提供了 XPath 表达式:

//*[not(ancestor-or-self::IN1_Insurance)]|//IN1_Insurance[2]/descendant-or-self::* 

选择文档中的所有元素。

注意:

所描述的任务是使用 XSLT 完成的基本任务。

最后:如果允许您使用 C#,那么您可以使用 XslCompiledTransform(或 XslTransform)类。使用其 Transform() 方法对 XML 文档执行以下转换:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

   <xsl:template match="node()|@*">
       <xsl:copy>
           <xsl:apply-templates select="node()|@*"/>
       </xsl:copy>
   </xsl:template>

   <xsl:template match="insurance[not(id=1)]"/>
</xsl:stylesheet>

这会产生想要的结果:

<root>
    <Patient>
        <PatientId></PatientId>
        <name></name>
    </Patient>
    <insurance>
        <id>1</id>
        <billing></billing>
    </insurance>
</root>

【讨论】:

    猜你喜欢
    • 2023-04-04
    • 2016-11-27
    • 1970-01-01
    • 2017-10-17
    • 1970-01-01
    • 2012-12-07
    • 2019-03-25
    • 1970-01-01
    • 2010-11-27
    相关资源
    最近更新 更多