【问题标题】:c# Merging two XMLs giving errorc# 合并两个 XML 给出错误
【发布时间】:2025-12-14 14:05:01
【问题描述】:

我正在尝试将两个结构相同但数据不同的 XML 合并为一个。

我收到此错误:A node of type Document cannot be added to content.

下面是我的代码

var productElements =
    testGroupProvider.GetTestGroup().ProductTests.Select(
        productTest => new XElement(xNamespace + "Product",
            new XElement(xNamespace + "ExternalId", productTest.ProductNameKey),
            new XElement(xNamespace + "Name", testGroupProvider.GetProductName(productTest)),
            new XElement(xNamespace + "ImageUrl", ChoiceBaseHostName + GetProductImageUrl(productTest, TargetDatabase))));

var root = new XDocument(
    new XElement(xNamespace + "Feed",
        new XAttribute("xmlns", xNamespace),
        new XAttribute("name", BVFeedsName),
        new XAttribute("incremental", "true"),
        new XAttribute("extractDate", DateTime.Now.ToString("o")),
        new XElement(xNamespace + "Categories",
            new XElement(xNamespace + "Category",
                new XElement(xNamespace + "ExternalId", testGroupProvider.GetProductGroup().Id),
                new XElement(xNamespace + "Name", testGroupProvider.GetProductGroup().Name),
        testGroupProvider.GetTestGroup().Name),
        new XElement(xNamespace + "Products", productElements)));


var filePath = @"D:\testXML\test.xml";
XElement xml = XElement.Load(filePath);
xml.Add(root);
xml.Save(filePath);

谁能告诉我我做错了什么。

这是 test.xml 中的 XML 结构

<?xml version="1.0" encoding="utf-8"?>
<Feed xmlns="http://www.bazaarvoice.com/xs/PRR/ProductFeed/5.6" name="Choice" incremental="true" extractDate="2016-07-12T15:24:44.5732750+10:00">
  <Categories>
    <Category>
      <ExternalId>{09B3B4FB-F5CF-4522-BE96-4C4B535580C3}</ExternalId>
      <Name>Cereal and muesli</Name>
    </Category>
  </Categories>
  <Products>
    <Product>
      <ExternalId>coles-almond-hazelnut-macadamia-cluster-fusions</ExternalId>
      <Name>Coles Almond, Hazelnut &amp; Macadamia Cluster Fusions</Name>
       <ImageUrl></ImageUrl>
    </Product>
  </Products>
</Feed>

第二个XML结构相同,产品不同

<?xml version="1.0" encoding="utf-8"?>
<Feed xmlns="http://www.bazaarvoice.com/xs/PRR/ProductFeed/5.6" name="Choice" incremental="true" extractDate="2016-07-12T15:24:44.5732750+10:00">
  <Categories>
    <Category>
      <ExternalId>{12}</ExternalId>
      <Name>cat1</Name>
    </Category>
  </Categories>
  <Products>
    <Product>
      <ExternalId>Id</ExternalId>
      <Name>Ccoles</Name>
       <ImageUrl></ImageUrl>
    </Product>
  </Products>
</Feed>

我想像下面这样组合它们

  <?xml version="1.0" encoding="utf-8"?>
    <Feed xmlns="http://www.bazaarvoice.com/xs/PRR/ProductFeed/5.6" name="Choice" incremental="true" extractDate="2016-07-12T15:24:44.5732750+10:00">
      <Categories>
        <Category>
          <ExternalId>{09B3B4FB-F5CF-4522-BE96-4C4B535580C3}</ExternalId>
          <Name>Cereal and muesli</Name>
        </Category>
         <Category>
          <ExternalId>{12}</ExternalId>
          <Name>cat1</Name>
        </Category>
      </Categories>
      <Products>
        <Product>
          <ExternalId>coles-almond-hazelnut-macadamia-cluster-fusions</ExternalId>
          <Name>Coles Almond, Hazelnut &amp; Macadamia Cluster Fusions</Name>
           <ImageUrl></ImageUrl>
        </Product>
        <Product>
          <ExternalId>Id</ExternalId>
          <Name>Ccoles</Name>
           <ImageUrl></ImageUrl>
        </Product>
      </Products>
    </Feed>

【问题讨论】:

  • 足够清晰 - 文档在最上面。 xml.Add(root) 失败。
  • 嗯,错误消息似乎是不言自明的——元素不能包含文档。也许您应该创建root 就像XElement 一样?我们不知道您最终希望得到什么,或者现有文件中有什么,这无济于事。
  • @Tommy 我已经添加了 XML 结构
  • 但是你还没有告诉我们你想要的结果是什么。目前,您的代码只是尝试向现有文档添加一个节点。如果您的意思是实际上想要将一个文档中的所有类别添加到另一个文档中的类别中,以及产品的类别中,那是另一回事。
  • @JonSkeet 请检查一下,我已经添加了第二个 xml 和合并的 xml

标签: c# .net xml c#-4.0 xml-parsing


【解决方案1】:

xml 文档必须只有一个根

使用您附加的文档,您可以将 xml.Add(root); 替换为以下内容(即,它将一个根目录下的每个节点添加到另一个 xml 根目录)

foreach (var child in root.Root.Elements())
{
    xml.Element(child.Name.ToString()).Add(child.Nodes());
}

编辑 - 进一步概括

您可以使用 2 XElements 的 Merge 扩展来概括上述代码,使其如下所示

foreach (var child in root.Elements())
{
    xml.Element(child.Name.ToString()).Merge(child, xNamespace + "ExternalId");
}

已定义扩展

public static void Merge(this XElement root1, XElement root2, XName element_id)
{
    root1.Add(root2.Elements().Except(root1.Elements(), new MyComparer(element_id)));
}

带有 xml 比较器

public class MyComparer : IEqualityComparer<XElement>
{
    private XName _element_id;
    public MyComparer(XName element_id)
    {
        _element_id = element_id;
    }
    public bool Equals(XElement x, XElement y)
    {
        return x.Element(_element_id).Value.Equals(y.Element(_element_id).Value);
    }
    public int GetHashCode(XElement el)
    {
        return el.Element(_element_id).Value.GetHashCode();
    }
}

【讨论】:

    【解决方案2】:

    选择要添加的正确节点和要添加的正确节点。

    var filePath = @"D:\testXML\test.xml";
    XElement xml = XElement.Load(filePath);
    
    var xmlCategories = xml.Descendants("Categories").First();
    var rootCategories = root.Descendants("Category");
    xmlCategories.Add(rootCategories);
    
    var xmlProducts = xml.Descendants("Products").First();
    var rootProducts = root.Descendants("Product");
    xmlProducts.Add(rootProducts);
    
    xml.Save(filePath);
    

    清楚自己在做什么。

    【讨论】:

      【解决方案3】:

      试试这个

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.Xml;
      using System.Xml.Linq;
      
      
      namespace ConsoleApplication2
      {
          class Program
          {
              const string FILENAME1 = @"c:\temp\test1.xml";
              const string FILENAME2 = @"c:\temp\test2.xml";
              static void Main(string[] args)
              {
                  XDocument doc1 = XDocument.Load(FILENAME1);
                  XDocument doc2 = XDocument.Load(FILENAME2);
      
                  XElement category1 = doc1.Descendants().Where(x => x.Name.LocalName == "Categories").FirstOrDefault();
                  XElement category2 = doc2.Descendants().Where(x => x.Name.LocalName == "Categories").FirstOrDefault();
                  category1.Add(category2.Descendants());
      
      
                  XElement product1 = doc1.Descendants().Where(x => x.Name.LocalName == "Products").FirstOrDefault();
                  XElement product2 = doc2.Descendants().Where(x => x.Name.LocalName == "Products").FirstOrDefault();
                  product1.Add(product2.Descendants());
              }
          }
      
      
      }
      

      【讨论】:

        【解决方案4】:

        试试这个,对VB很抱歉

            'second is The second XML has the same structure with different products
        
            Dim combined As XElement = New XElement(test) 'create copy of test.xml
            combined.<Categories>.LastOrDefault.Add(second.<Categories>.Elements)
            combined.<Products>.LastOrDefault.Add(second.<Products>.Elements)
        

            'if test can be used to combine then
        
            test.<Categories>.LastOrDefault.Add(second.<Categories>.Elements)
            test.<Products>.LastOrDefault.Add(second.<Products>.Elements)
        

        结果是

        <Feed name="Choice" incremental="true" extractDate="2016-07-12T15:24:44.5732750+10:00" xmlns="http://www.bazaarvoice.com/xs/PRR/ProductFeed/5.6">
          <Categories>
            <Category>
              <ExternalId>{09B3B4FB-F5CF-4522-BE96-4C4B535580C3}</ExternalId>
              <Name>Cereal and muesli</Name>
            </Category>
            <Category>
              <ExternalId>{12}</ExternalId>
              <Name>cat1</Name>
            </Category>
          </Categories>
          <Products>
            <Product>
              <ExternalId>coles-almond-hazelnut-macadamia-cluster-fusions</ExternalId>
              <Name>Coles Almond, Hazelnut &amp; Macadamia Cluster Fusions</Name>
              <ImageUrl></ImageUrl>
            </Product>
            <Product>
              <ExternalId>Id</ExternalId>
              <Name>Ccoles</Name>
              <ImageUrl></ImageUrl>
            </Product>
          </Products>
        </Feed>
        

        我使用的测试数据是

            Dim test As XElement =
                <Feed xmlns="http://www.bazaarvoice.com/xs/PRR/ProductFeed/5.6" name="Choice" incremental="true" extractDate="2016-07-12T15:24:44.5732750+10:00">
                    <Categories>
                        <Category>
                            <ExternalId>{09B3B4FB-F5CF-4522-BE96-4C4B535580C3}</ExternalId>
                            <Name>Cereal and muesli</Name>
                        </Category>
                    </Categories>
                    <Products>
                        <Product>
                            <ExternalId>coles-almond-hazelnut-macadamia-cluster-fusions</ExternalId>
                            <Name>Coles Almond, Hazelnut &amp; Macadamia Cluster Fusions</Name>
                            <ImageUrl></ImageUrl>
                        </Product>
                    </Products>
                </Feed>
        
            Dim second As XElement =
                <Feed xmlns="http://www.bazaarvoice.com/xs/PRR/ProductFeed/5.6" name="Choice" incremental="true" extractDate="2016-07-12T15:24:44.5732750+10:00">
                    <Categories>
                        <Category>
                            <ExternalId>{12}</ExternalId>
                            <Name>cat1</Name>
                        </Category>
                    </Categories>
                    <Products>
                        <Product>
                            <ExternalId>Id</ExternalId>
                            <Name>Ccoles</Name>
                            <ImageUrl></ImageUrl>
                        </Product>
                    </Products>
                </Feed>
        

        XElements 可以这样加载

            test = XElement.Load("PATH")
            second = XElement.Load("second PATH")
        

        并像这样保存

            test.Save("PATH")
            second.Save("second PATH")
            combined.Save("combined PATH")
        

        【讨论】: