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

时间:2018-02-07 09:03:09

标签: c# .net xml xsd xmlserializer

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

这是问题How to deserialize a node in a large document using XmlSerializer的后续内容,并使用与该问题中描述的相同的xsd和生成的类。

让我们说,在我的样本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:...属性。他们来自哪里,我怎么能摆脱他们?

1 个答案:

答案 0 :(得分:1)

正如 Omitting all xsi and xsd namespaces when serializing an object in .NET? 中所述,XmlSerializer将在序列化时始终有助于添加xsixsd命名空间。如果您不想要,则需要调用Serialize的重载,其中可以指定所需的初始命名空间,例如: 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