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
- 获取所有名为 "author" 的元素:
doc.Root.Elements("author")
使用名为 "name" 的属性值对元素进行分组,并插入名为 "book" 的元素列表。由于这些元素位于另一个名为 "books" 的元素下,我们希望获得作者标签的后代而不是直接子元素,这就是 Elements()
所使用的。 lambda 表达式
a => a.Attribute("name").Value
获取我们分组的值,但我们知道如果缺少 "name" 标签,这会抛出 NullReferenceException
a => a.Descendants("book")
获取作者标签下某处 名为"book" 的元素。这允许我们跳过必须直接指定 "books" 标签(a.Element("books").Elements("book")
是说同样的话的长方法)
创建一个包含分组元素的 IEnumerable<XElement>
。 g.Key
是我们分组的作者姓名,g
是该键下分组对象的 IEnumerable
。
- 最后,我们创建一个根节点并在其下添加我们所有的新元素!
试试这个
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();
}
}
}
}
}
我正在尝试将 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
- 获取所有名为 "author" 的元素:
doc.Root.Elements("author")
使用名为 "name" 的属性值对元素进行分组,并插入名为 "book" 的元素列表。由于这些元素位于另一个名为 "books" 的元素下,我们希望获得作者标签的后代而不是直接子元素,这就是
Elements()
所使用的。 lambda 表达式a => a.Attribute("name").Value
获取我们分组的值,但我们知道如果缺少 "name" 标签,这会抛出NullReferenceException
a => a.Descendants("book")
获取作者标签下某处 名为"book" 的元素。这允许我们跳过必须直接指定 "books" 标签(a.Element("books").Elements("book")
是说同样的话的长方法)
创建一个包含分组元素的
IEnumerable<XElement>
。g.Key
是我们分组的作者姓名,g
是该键下分组对象的IEnumerable
。- 最后,我们创建一个根节点并在其下添加我们所有的新元素!
试试这个
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();
}
}
}
}
}