简体   繁体   English

XML迭代和重新排列-C#

[英]XML iteration and rearrangement- C#

I Have an XML file like the one below: 我有一个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>

I need to read it, and re-arrange it to look: 我需要阅读它,然后重新排列以使其看起来:

<?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>

Basically, the original XML has separate Elements (LP1, X-1, HP1) that I want to put as sub elements when the equipment "FromName" matches the parent element name of the system. 基本上,原始XML具有单独的元素(LP1,X-1,HP1),当设备“ FromName”与系统的父元素名称匹配时,我希望将它们作为子元素放入。

I am guessing that I will need to do some recursive function, but I am kind of new to C# and programming in general and haven't had much experience with XML or recursive function. 我猜想我将需要做一些递归函数,但是我对C#和一般编程还是有点陌生​​,对XML或递归函数没有太多的经验。

Any help would be appreciated. 任何帮助,将不胜感激。

Thank you 谢谢

There are more than a few tutorials on XML file manipulation. 关于XML文件操作的教程不止几个。 Example of both save & load (reversed for tutorial, but both there): 保存和加载的示例(在教程中相反,但两者都存在):

http://www.java2s.com/Code/CSharp/XML/Loadxmldocumentfromxmlfile.htm

The steps should be roughly... 步骤应大致...

  • open Input file 打开输入文件
  • Load input into XmlNodeList 将输入加载到XmlNodeList
  • Parse input into ouput XmlNodeList() 将输入解析为输出XmlNodeList()
  • Save output into new file. 将输出保存到新文件中。

I actually think I see what your wanting, after a bit of staring... I'd have to tink about the parse step for a bit personally. 我实际上想一想,经过一番凝视,我明白了您的需求...我个人必须对解析步骤进行一些思考。

If Xml output is what you're after, Xslt is probably your best bet. 如果您要使用Xml输出,那么Xslt可能是最好的选择。 You'll need to load the xml and stylesheet into memory and then process accordingly. 您需要将xml和样式表加载到内存中,然后进行相应处理。 Something like: 就像是:

// 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();

Regarding the stylesheet, I'm assuming that you'd be taking the last node in the xml file and then nesting the Equipment nodes from above. 关于样式表,我假设您要获取xml文件中的最后一个节点,然后从上方嵌套“设备”节点。 In order to do the recursive lookups, you'll need a special xslt feature that ships as part of the MSXML parser, specifically ms: node-set . 为了进行递归查找,您需要一个特殊的xslt功能,该功能作为MSXML解析器的一部分提供,特别是ms: node-set This function let's you perform an xpath query and return a node rather than raw text. 通过此函数,您可以执行xpath查询并返回节点而不是原始文本。

Tip : Xslt is processed from the bottom up, so scroll down and then read-up. 提示 :Xslt是从下至上处理的,因此请向下滚动然后再向上阅读。

PS -- I'm doing this from memory. PS-我正在从内存中做到这一点。 Xslt can be finicky so you may want to play with this a bit. 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>

While it can be definitely compressed to a one-liner as Steven suggested :) I chose to sprawl around a bit to make it more understandable. 正如史蒂文(Steven)建议的那样,虽然可以肯定地将其压缩为单一格式:)我选择了四处扩展以使其更易于理解。 Of course I still failed so I'll also explain a bit. 当然我仍然失败了,所以我也要解释一下。

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();

Assuming xml is the string in the OP, I simply parsed an XDocument from it. 假设xml是OP中的字符串,我只是从中解析出一个XDocument。 Then, a simple shortcut for getting XNames, since the code would have been even more crowded with it inline. 然后,这是获取XNames的简单捷径,因为代码内联时可能更加拥挤。

We get all the child elements of System and store it for later; 我们获取System的所有子元素并将其存储以备后用; for lack of a better term I called them systems . 由于缺乏更好的称呼,我称之为systems If there are multiple levels on which these elements may appear, the logic will of course need to be adjusted to find them all reliably. 如果这些元素可能出现在多个级别上,则当然需要调整逻辑以可靠地找到它们。

Then we iterate through all the elements with the name Equipment ( equipments ), get the FromName element value, and search for an element with the same name in systems . 然后我们通过所有的名称设备(元素迭代equipments ),得到FROMNAME元素值,并搜索与相同名称的元素systems If we find it, we simply add it to the current element and remove it from its parent; 如果找到它,我们只需将其添加到当前元素并将其从其父元素中删除; since the elements are still all part of the x tree, it works as expected. 由于元素仍然是x树的一部分,因此可以正常工作。

Aaand... done. Aaand ...完成。 result is the desired result posted by the OP. result是OP发布的期望结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM