C# 从另一个 XML 创建嵌套的 XML

C# create nested XML from another XML

我正在尝试将 XML 转换为具有元素属性的嵌套 XML。我在谷歌上搜索了很多,并在这里查看了一些问题和答案,但我仍然无法理解它。

我想使用 C#、linq 将子节点分组到同一作者名下 xml。

样本XML:

<authors>
  <author name="John">
    <books>
       <book type="Children">ABC</book>
    </books>
    <published> ---<new
      <print year="2011"> ---<new
         <publisher>Msoft</publisher> ---<new
      </print> ---<new
    </published> ---<new
  </author>
  <author name="May">
    <books>
       <book type="Children">A beautiful day</book>
    </books>
    <published> ---<new
      <print year="2011"> ---<new
         <publisher>hardsoft</publisher> ---<new
      </print> ---<new
    </published> ---<new
  </author>
  <author name="John">
    <books>
       <book type="Fiction">BBC</book>
    </books>
    <published> ---<new
      <print year="2013"> ---<new
         <publisher>dsney</publisher> ---<new
      </print> ---<new
    </published> ---<new
  </author>
</authors>

预期输出:

<authors>
  <author name="John">
    <books>
       <book type="Children">ABC</book>
       <book type="Fiction">BBC</book>
    </books>
    <published>
      <print year="2011">
         <publisher>Msoft</publisher>
         <publisher>hardsoft</publisher>
      </print>
    </published>
  </author>
  <author name="May">
    <books>
       <book type="Children">A beautiful day</book>
    </books>
    <published>
      <print year="2013">
         <publisher>dsney</publisher>
      </print>
    </published>
  </author>
</authors>

如果有其他具有属性的节点需要在同一作者下分组,例如我应该添加另一个分组还是 select 上一组的元素?

到目前为止,我已经尝试过:

XDocument doc = XDocument.Load(pathtoxmlfile);
var query = from e in doc.Elements("author")
        group e by e.Attribute("name").Value into g
        select new XElement("author", new XAttribute("name", g.Key),
               new XElement("books", 
                   g.Select(x => x.Element("books").Elements("book"))
                   , new XElement("published",
                         g.Select(y=>y.Elements("publisher")
                   )
            )
        )
 );

 XElement root = new XElement("authors", query);

只输出我里面和没有入口的作者节点

<author>
  <books>...this part is output as expect...
  </books>
  <published>
    <publisher />
  </published>
</author>
string xml = @"<authors>
  <author name=""John"">
    <books>
       <book type=""Children"">ABC</book>
    </books>
  </author>
  <author name=""May"">
    <books>
       <book type=""Children"">A beautiful day</book>
    </books>
  </author>
  <author name=""John"">
    <books>
       <book type=""Fiction"">BBC</book>
    </books>
  </author>
</authors>";

XElement root = XElement.Parse(xml);
var query = from e in root.Elements("author")
            group e by e.Attribute("name").Value into g
            select new XElement("author", new XAttribute("name", g.Key),
                   new XElement("books", 
                                 g.Select(x => x.Element("books").Elements("book")).ToArray()));

 XElement newRoot = new XElement("authors", query.ToArray());
 Console.WriteLine(newRoot);

假设在本示例中,您已经在名为 ungrouped.xml 的文档中拥有未分组的 XML。

XDocument doc = XDocument.Load(ungrouped.xml);
var groupedAuthors = doc.Root.Elements("author")
                        .GroupBy(a => a.Attribute("name").Value, 
                                 a => a.Descendants("book"))
                        .Select(g => new XElement("author", new XAttribute("name", g.Key,
                                                            new XElement("books", g.ToArray())
                                                 )
                                );



XElement root = new XElement("authors", groupedAuthors);

让我们逐步完成上面的代码来解释这里发生了什么。

首先是我们在您的输入示例中使用未组织的 XML 文件加载 XDocument 对象。然后我们开始使用 Linq to XML stuff

  1. 获取所有名为 "author" 的元素:doc.Root.Elements("author")
  2. 使用名为 "name" 的属性值对元素进行分组,并插入名为 "book" 的元素列表。由于这些元素位于另一个名为 "books" 的元素下,我们希望获得作者标签的后代而不是直接子元素,这就是 Elements() 所使用的。 lambda 表达式

    • a => a.Attribute("name").Value 获取我们分组的值,但我们知道如果缺少 "name" 标签,这会抛出 NullReferenceException
    • a => a.Descendants("book") 获取作者标签下某处 名为"book" 的元素。这允许我们跳过必须直接指定 "books" 标签(a.Element("books").Elements("book") 是说同样的话的长方法)
  3. 创建一个包含分组元素的 IEnumerable<XElement>g.Key 是我们分组的作者姓名,g 是该键下分组对象的 IEnumerable

  4. 最后,我们创建一个根节点并在其下添加我们所有的新元素!

试试这个

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;


namespace ConsoleApplication34
{
    class Program
    {

        static void Main(string[] args)
        {
            string input =
                "<authors>" +
                  "<author name=\"John\">" +
                    "<books>" +
                       "<book type=\"Children\">ABC</book>" +
                    "</books>" +
                  "</author>" +
                  "<author name=\"May\">" +
                    "<books>" +
                       "<book type=\"Children\">A beautiful day</book>" +
                    "</books>" +
                  "</author>" +
                  "<author name=\"John\">" +
                    "<books>" +
                       "<book type=\"Fiction\">BBC</book>" +
                    "</books>" +
                  "</author>" +
                "</authors>";

            XElement element = XElement.Parse(input);
            var authors = element.Descendants("author").GroupBy(x => x.Attribute("name").Value).ToList();
            foreach (var author in authors)
            {
                var books = author.Descendants("books");
                for (int i = author.Count() - 1; i >= 1 ; i--)
                {
                    var book = author.Skip(i).FirstOrDefault().Descendants("book");
                    books.Elements("book").First().Add(book);
                    author.Skip(i).DescendantNodesAndSelf().Remove();
                }
            }
        }

    }

}