我有一个类似下面的XML文件:
<?xml version="1.0" ?>
<System>
<LP1>
<Equipment>
<FromName>Receptacle</FromName>
<Wire>1-#10, 1-#10, 1-#10</Wire>
<Length>89.8411846136344</Length>
</Equipment>
</LP1>
<X-1>
<Equipment>
<FromName>LP1</FromName>
<Wire>3-#3/0, 1-#3/0, 1-#6</Wire>
<Length>10.170412377555</Length>
</Equipment>
</X-1>
<HP1>
<Equipment>
<FromName>X-1</FromName>
<Wire>3-#3/0, 1-#3/0, 1-#6</Wire>
<Length>8.2423259796908</Length>
</Equipment>
<Equipment>
<FromName>AH-1</FromName>
<Wire>3-#6, 1-#10</Wire>
<Length>32.4019419736209</Length>
</Equipment>
<Equipment>
<FromName>EF-1</FromName>
<Wire>3-#12, 1-#12, 1-#12</Wire>
<Length>8.33572105849677</Length>
</Equipment>
</HP1>
</System>
我需要阅读它,并重新安排它看起来:
<?xml version="1.0" ?>
<HP1>
<Equipment>
<FromName>X-1</FromName>
<Wire>3-#3/0, 1-#3/0, 1-#6</Wire>
<Length>8.2423259796908</Length>
<Equipment>
<FromName>LP1</FromName>
<Wire>3-#3/0, 1-#3/0, 1-#6</Wire>
<Length>10.170412377555</Length>
<Equipment>
<FromName>Receptacle</FromName>
<Wire>1-#10, 1-#10, 1-#10</Wire>
<Length>89.8411846136344</Length>
</Equipment>
</Equipment>
</Equipment>
<Equipment>
<FromName>AH-1</FromName>
<Wire>3-#6, 1-#10</Wire>
<Length>32.4019419736209</Length>
</Equipment>
<Equipment>
<FromName>EF-1</FromName>
<Wire>3-#12, 1-#12, 1-#12</Wire>
<Length>8.33572105849677</Length>
</Equipment>
</HP1>
</System>
基本上,当设备“FromName”与系统的父元素名称匹配时,原始XML具有单独的元素(LP1,X-1,HP1),我想将其作为子元素放置。
我猜我需要做一些递归函数,但我对C#和编程很新,并且对XML或递归函数没有多少经验。
任何帮助都将不胜感激。
谢谢
答案 0 :(得分:0)
有很多关于XML文件操作的教程。保存和放大的示例加载(对于教程反向,但两者都有):
http://www.java2s.com/Code/CSharp/XML/Loadxmldocumentfromxmlfile.htm
步骤应该大致......
我实际上认为我看到了你想要的东西,经过一番凝视......我不得不亲自尝试一下解析步骤。
答案 1 :(得分:0)
如果您正在使用Xml输出,那么Xslt可能是您最好的选择。您需要将xml和样式表加载到内存中,然后进行相应的处理。类似的东西:
// load the stylesheet
XslTransform stylesheet = new XslTransform();
stylesheet.Load(xsltFilePath);
// load the xml
XPathDocument doc = new XPathDocument(xmlFilePath);
//create the output stream
XmlTextWriter myWriter = new XmlTextWriter("output.xml", null);
// autobots! Transform!
stylesheet.Transform(doc, null, myWriter);
myWriter.Close();
关于样式表,我假设您将获取xml文件中的最后一个节点,然后从上面嵌套Equipment节点。为了进行递归查找,您需要一个特殊的xslt功能,该功能作为MSXML解析器的一部分提供,特别是ms:node-set。此函数允许您执行xpath查询并返回节点而不是原始文本。
提示:Xslt从下往上处理,向下滚动然后再读。
PS - 我是从记忆中做到的。 Xslt可能很挑剔,所以你可能想要玩这个。
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ms="urn:schemas-microsoft-com:xslt"
>
<!-- wildcard: other content is copied as is -->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>
<!-- this is our magic recursive template. Any element that matches "Equipment"
will caught and processed here. Everything else will default to our
Wildcard above -->
<xsl:template match="Equipment">
<!-- Read the FromName element into the variable $fromName -->
<xsl:variable name="fromName" select="FromName/text()" />
<!-- Manually reconstruct the Equipment Node -->
<Equipment>
<!-- copy out FromName, Wire and Length -->
<xsl:copy-of select="FromName" />
<xsl:copy-of select="Wire" />
<xsl:copy-of select="Length" />
<!-- this is how we recursively pull our Element nodes in, which
will match on this template -->
<xsl:apply-templates select="ms:node-set('//' + $fromName')/Equipment" />
</Equipment>
</xsl:template>
<!-- Starting point: Find the last node under system -->
<xsl:template match="/System/*[last()]">
<!-- copy the elements and attributes of this node to the output stream -->
<xsl:copy>
<!-- match templates on its contents -->
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
答案 2 :(得分:0)
虽然史蒂文建议它可以肯定地压缩成一个单行程,但我选择了一点点扩展以使其更容易理解。当然我还是失败了所以我也会解释一下。
XDocument x = XDocument.Parse(xml);
Func<string, XName> xn = s => XName.Get(s, "");
var systems = x.Elements().First();
var equipments = x.Descendants(xn("Equipment"));
equipments.ToList().ForEach(e =>
{
string fromName = e.Element(xn("FromName")).Value;
var found = systems.Element(xn(fromName));
if (found != null)
{
e.Add(found.Elements(xn("Equipment")));
found.Remove();
};
});
string result = x.ToString();
假设xml
是OP中的字符串,我只是从中解析了一个XDocument。然后,获取XNames的简单快捷方式,因为代码内联更加拥挤。
我们获取System的所有子元素并将其存储以供日后使用;由于缺乏更好的术语,我称之为systems
。如果这些元素可能出现多个级别,那么逻辑当然需要进行调整以便可靠地找到它们。
然后我们遍历名称为Equipment(equipments
)的所有元素,获取FromName元素值,并在systems
中搜索具有相同名称的元素。如果我们找到它,我们只需将它添加到当前元素并将其从父元素中删除;因为元素仍然是x
树的所有部分,所以它按预期工作。
Aaand ......完成了。 result
是OP发布的预期结果。