xpath返回字符串而不是nodelist

时间:2010-11-22 15:18:21

标签: c# xslt biztalk xpath

我正在开发一个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>"

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

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

我可以使用的代码是C#。 我试图将节点列表分配给xmldocument,但这会抛出一个强制转换错误(显然是..)。

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

  • 将节点列表分配给xmldocument而没有循环(我认为在C#中不可能)
  • 以某种方式将nodelist转换为string并在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#code ..

2 个答案:

答案 0 :(得分:3)

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

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

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

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

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

直接进入新的XML文档。如果你发布一些样本输入和所需的输出,我们可以帮助确定是否是这种情况。

<强>更新

我看到你要做的事情:复制一个XML文档,省略除你想要的所有<insurance>元素(及其后代)。

这可以在没有循环的情况下完成如果输出与样本输出一样简单:只有一个<Patient>和一个<insurance>元素及其后代,在一个下面顶级元素。

类似的东西(我无法测试,因为我没有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,所以我将[0]添加到上面的ImportNode()的第一个参数。感谢@Martin Honnen警告我。

答案 1 :(得分:1)

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>