如何使用c#代码LINQ将多层XML展平为单层XML

本文关键字:XML 单层 何使用 代码 LINQ | 更新日期: 2023-09-27 17:58:57

我读了一些关于扁平化XML结构的文章,这样每个元素及其值都会变成根元素上的属性。然而,我的要求是按照下面的示例来压平输入XML,对此我找不到任何帮助。

我有一个XML结构,如下所示,

<RATES ID="1" RatesEffectivateDate="27/02/2014">
  <Type Name="Type1" Code="A">
    <Del ID="D1">
      <Field1>10</Field1>
      <Field2>20</Field2>
      <Field3>30</Field3>
      <Field4>40</Field4>
    </Del>
    <Del ID="D2">
      <Field1>50</Field1>
      <Field2>60</Field2>
      <Field3>70</Field3>
      <Field4>80</Field4>
    </Del>
  </Type>
  <Type Name="Type2" Code="B">
    <Del ID="D1">
      <Field1>110</Field1>
      <Field2>120</Field2>
      <Field3>130</Field3>
      <Field4>140</Field4>
    </Del>
    <Del ID="D3">
      <Field1>150</Field1>
      <Field2>160</Field2>
      <Field3>170</Field3>
      <Field4>180</Field4>
    </Del>
  </Type>
</RATES>

这需要标准化为以下格式,

<RATES>
  <RATE>
    <ID>Type1</ID>
    <Code>A</Code>
    <DelID>D1</DelID>
    <Field1>10</Field1>
  </RATE>
  <RATE>
    <ID>Type1</ID>
    <Code>A</Code>
    <DelID>D1</DelID>
    <Field2>20</Field2>
  </RATE>
  <RATE>
    <ID>Type1</ID>
    <Code>A</Code>
    <DelID>D1</DelID>
    <Field3>30</Field3>
  </RATE>
  <RATE>
    <ID>Type1</ID>
    <Code>A</Code>
    <DelID>D1</DelID>
    <Field4>40</Field4>
  </RATE>
  <RATE>
    <ID>Type1</ID>
    <Code>A</Code>
    <DelID>D2</DelID>
    <Field1>50</Field1>
  </RATE>
  <RATE>
    <ID>Type1</ID>
    <Code>A</Code>
    <DelID>D2</DelID>
    <Field2>60</Field2>
  </RATE>
  <RATE>
    <ID>Type1</ID>
    <Code>A</Code>
    <DelID>D2</DelID>
    <Field3>70</Field3>
  </RATE>
  <RATE>
    <ID>Type1</ID>
    <Code>A</Code>
    <DelID>D2</DelID>
    <Field4>80</Field4>
  </RATE>
  <RATE>
    <ID>Type2</ID>
    <Code>B</Code>
    <DelID>D1</DelID>
    <Field1>110</Field1>
  </RATE>
  <RATE>
    <ID>Type2</ID>
    <Code>B</Code>
    <DelID>D1</DelID>
    <Field2>120</Field2>
  </RATE>
  <RATE>
    <ID>Type2</ID>
    <Code>B</Code>
    <DelID>D1</DelID>
    <Field3>130</Field3>
  </RATE>
  <RATE>
    <ID>Type2</ID>
    <Code>B</Code>
    <DelID>D1</DelID>
    <Field4>140</Field4>
  </RATE>
  <RATE>
    <ID>Type2</ID>
    <Code>B</Code>
    <DelID>D3</DelID>
    <Field1>50</Field1>
  </RATE>
  <RATE>
    <ID>Type2</ID>
    <Code>B</Code>
    <DelID>D3</DelID>
    <Field2>160</Field2>
  </RATE>
  <RATE>
    <ID>Type2</ID>
    <Code>B</Code>
    <DelID>D3</DelID>
    <Field3>170</Field3>
  </RATE>
  <RATE>
    <ID>Type2</ID>
    <Code>B</Code>
    <DelID>D3</DelID>
    <Field4>180</Field4>
  </RATE>
</RATES>

由于我是LINQ的新手,请对此提出建议。谢谢

如何使用c#代码LINQ将多层XML展平为单层XML

我不知道是否有任何内置函数可以对XML进行扁平化,但我非常担心它们能够满足您的需求,因为根据您的示例,必须忽略某些属性。

您最好使用LINQ to XML,这样可以非常容易地转换此类文档,而且不需要太多代码。查看我的解决方案:

string origXml = "<RATES ID='"1'" RatesEffectivateDate='"27/02/2014'">'n  <Type Name='"Type1'" Code='"A'">'n    <Del ID='"D1'">'n      <Field1>10</Field1>'n      <Field2>20</Field2>'n      <Field3>30</Field3>'n      <Field4>40</Field4>'n    </Del>'n    <Del ID='"D2'">'n      <Field1>50</Field1>'n      <Field2>60</Field2>'n      <Field3>70</Field3>'n      <Field4>80</Field4>'n    </Del>'n  </Type>'n  <Type Name='"Type2'" Code='"B'">'n    <Del ID='"D1'">'n      <Field1>110</Field1>'n      <Field2>120</Field2>'n      <Field3>130</Field3>'n      <Field4>140</Field4>'n    </Del>'n    <Del ID='"D3'">'n      <Field1>150</Field1>'n      <Field2>160</Field2>'n      <Field3>170</Field3>'n      <Field4>180</Field4>'n    </Del>'n  </Type>'n</RATES>";
var xDoc = XDocument.Parse(origXml);
var resDoc = new XDocument(
    new XElement("RATES",
                    xDoc.Element("RATES")
                        .Elements("Type")
                        .SelectMany(typeEl =>
                                    typeEl.Elements("Del")
                                        .SelectMany(delEl =>
                                                    delEl.Elements()
                                                        .Select(fieldEl =>
                                                                new XElement("RATE",
                                                                                new XElement("ID", typeEl.Attribute("Name").Value),
                                                                                new XElement("Code", typeEl.Attribute("Code").Value),
                                                                                new XElement("DelID", delEl.Attribute("ID").Value),
                                                                                new XElement(fieldEl.Name, fieldEl.Value)))))
        ));
resDoc.Save("transformedDoc.xml", SaveOptions.None);

输出正是您为示例提供的内容。

你可以这样做:

var xmlDocument = XDocument.Load("path");
var rates = new List<XElement>();
foreach (var type in xmlDocument.Descendants("Type"))
{
    foreach (var del in type.Elements("Del"))
    {
          foreach (var field in del.Elements())
          {
              XElement rate = new XElement("RATE",
                        new XElement("ID", (string) type.Attribute("Name")),
                        new XElement("Code", (string) type.Attribute("Code")),
                        new XElement("DelID", (string) del.Attribute("ID")),
                        new XElement(field.Name, (string) field));
              rates.Add(rate);
          }
    }
}
XElement root = new XElement("RATES");
root.Add(rates);
root.Save("newFile.xml");

使用LINQ代替循环的另一种方法,但我认为这是可读性较差的

var xmlDocument = XDocument.Load("path");
var newXML = new XElement("RATES",
            xmlDocument.Descendants()
                .Where(x => x.Name.ToString().StartsWith("Field"))
                .Select(
                    x =>
                        new XElement("RATE",
                            new XElement("ID", (string) x.Parent.Parent.Attribute("Name")),
                            new XElement("Code", (string) x.Parent.Parent.Attribute("Code")),
                            new XElement("DelID", (string) x.Parent.Attribute("ID")),
                            new XElement(x.Name, (string) x))));
newXML.Save("newFile.xml");