【问题标题】:How to insert a node in a large document using XmlSerializer如何使用 XmlSerializer 在大文档中插入节点
【发布时间】:2018-07-17 11:59:05
【问题描述】:

我有一个大型 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:... 属性。它们是从哪里来的,我该如何摆脱它们?

【问题讨论】:

    标签: c# .net xml xsd xmlserializer


    【解决方案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

    【讨论】:

    • 非常感谢。我假设 xsd.exe 添加到自动生成的类的属性就足够了。
    • @Joe - 使用自动生成的类生成的 XML 完全有效且格式正确。它没有你想要的那么紧凑。
    • 这是一个很好的答案,您对链接相关问题的回答也是如此。我特别感谢您不厌其烦地创建了一个有效的 .Net fiddle。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-09
    • 2018-12-15
    • 1970-01-01
    • 2021-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多