我正在开发一个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,但这会抛出一个强制转换错误(显然是..)。
我看到它的方式是我有两个解决方案:
感谢您的帮助
编辑:
示例输入:
<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 ..
答案 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>