如何使用 XmlSerializer 在大型文档中插入节点

How to insert a node in a large document using XmlSerializer

我有一个很大的 XML 文档,我想使用 XmlSerializer class 插入新元素,其内容来自使用生成的 .NET class 实例xsd.exe。

这是问题 的跟进,并使用与该问题中描述的相同 xsd 和生成的 classes。

假设在我的示例中 XML 我想将我的福特汽车换成宝马。我试过以下代码:

static string XmlContent = @"
    <RootNode xmlns=""http://MyNamespace"">
        <Cars>
        <Car make=""Volkswagen"" />
        <Car make=""Ford"" />
        <Car make=""Opel"" />
        </Cars>
    </RootNode>";

private static void TestWriteMcve()
{
    var doc = new XmlDocument();
    doc.LoadXml(XmlContent);
    var nsMgr = new XmlNamespaceManager(doc.NameTable);
    nsMgr.AddNamespace("myns", "http://MyNamespace");

    var node = doc.DocumentElement.SelectSingleNode("myns:Cars/myns:Car[@make='Ford']", nsMgr);
    var parent = node.ParentNode;
    var carSerializer = new XmlSerializer(typeof(Car));

    using (var writer = node.CreateNavigator().InsertAfter())
    {
        // WriteWhitespace needed to avoid error "WriteStartDocument cannot
        // be called on writers created with ConformanceLevel.Fragment."
        writer.WriteWhitespace("");
        var newCar = new Car { make = "BMW" };
        carSerializer.Serialize(writer, newCar);
    }
    parent.RemoveChild(node);
    Console.WriteLine(parent.OuterXml);
}

我得到的结果接近我想要的:

<Cars xmlns="http://MyNamespace">
  <Car make="Volkswagen" />
  <Car xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" make="BMW" xmlns="" />
  <Car make="Opel" />
</Cars>

添加的元素上所有不需要的 xmlns:... 属性除外。它们从何而来,我该如何摆脱它们?

Omitting all xsi and xsd namespaces when serializing an object in .NET?, XmlSerializer will always helpfully add the xsi and xsd namespaces when serializing. If you don't want that, you need to call an overload of Serialize where the required initial namespaces can be specified, e.g. XmlSerializer.Serialize(XmlWriter, Object, XmlSerializerNamespaces) 中所述。以下扩展方法可以解决问题:

public static class XmlNodeExtensions
{
    public static XmlNode ReplaceWithSerializationOf<T>(this XmlNode node, T replacement)
    {
        if (node == null)
            throw new ArgumentNullException();
        var parent = node.ParentNode;
        var serializer = new XmlSerializer(replacement == null ? typeof(T) : replacement.GetType());

        using (var writer = node.CreateNavigator().InsertAfter())
        {
            // WriteWhitespace needed to avoid error "WriteStartDocument cannot
            // be called on writers created with ConformanceLevel.Fragment."
            writer.WriteWhitespace("");

            // Set up an appropriate initial namespace.
            XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
            ns.Add(node.GetNamespaceOfPrefix(node.NamespaceURI), node.NamespaceURI);

            // Serialize
            serializer.Serialize(writer, replacement, ns);
        }

        var nextNode = node.NextSibling;
        parent.RemoveChild(node);
        return nextNode;
    }
}

然后使用如下:

var newCar = new Car { make = "BMW" };
var node = doc.DocumentElement.SelectSingleNode("myns:Cars/myns:Car[@make='Ford']", nsMgr);
node = node.ReplaceWithSerializationOf(newCar);
var parent = node.ParentNode;

之后,为 doc 生成的 XML 将是:

<RootNode xmlns="http://MyNamespace">
  <Cars>
    <Car make="Volkswagen" />
    <Car make="BMW" />
    <Car make="Opel" />
  </Cars>
</RootNode>

工作示例 .Net fiddle