序列化 xml 文件的一部分。想要根上的命名空间,而不是序列化的子元素

Posted

技术标签:

【中文标题】序列化 xml 文件的一部分。想要根上的命名空间,而不是序列化的子元素【英文标题】:Serialize part of xml file. Want namespace on root, not on serialized subelements 【发布时间】:2015-02-17 21:25:57 【问题描述】:

我正在尝试制作一个类似于以下内容的 Xml 文件:

<RootLevel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.MyCompany.com/MySchema.xsd">
    <Level1>
        <Level2>
        </Level2>
    </Level1 >
    <Level1>
        <Level2>
        </Level2>
    </Level1 >
    etc. repeats hundreds of times
</RootLevel>

我使用 xsd.exe 实用程序从我的 xml 架构定义文件生成了一些类。它们看起来像:

[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://MyCompany.com/MySchema.xsd")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://www.MyCompany.com/MySchema.xsd", IsNullable = false)]
public partial class RootLevel

    private List<Level1> level1Field;

    public List<Level1> Level1Field 
    
        get  return this.level1Field;
        set this.level1Field = value;
    


[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.MyCompany.com/MySchema.xsd")]
public partial class Level1

    private List<Level2> level2Field;

    public List<Level2> Level2Field 
    
        get  return this.level2Field;
        set this.level2Field = value;
    

    /* other properties on Level1 go here*/


[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.MyCompany.com/MySchema.xsd")]
public partial class Level2

    /* properties on Level2 go here*/
    

我通过使用 XmlWriter.WriteStartElement() 写出 RootLevel 元素来创建此文件,然后通过创建 Level1 对象并使用 XmlSerializer 序列化它们来写出文件的其余部分。

目标

我希望文件只有 RootLevel 元素上的命名空间。

如果你有兴趣,这里是我目前尝试过的:

起点

一开始,我的 RootLevel 元素没有任何命名空间。 我的 Level1 和 Level2 元素有命名空间。

第 1 步:

我尝试通过覆盖 Level1 和 Level2 类的 XmlTypeAttributes 上的命名空间来从 Level1 和 Level2 元素中删除命名空间。

XmlTypeAttribute attribute = new XmlTypeAttribute();
attribute.Namespace = string.Empty;

XmlAttributes attributes = new XmlAttributes();
attributes.XmlType = attribute;

XmlAttributeOverrides overrides = new XmlAttributeOverrides();
overrides.Add(typeof(Level1), attributes);
overrides.Add(typeof(Level2), attributes);

this.xmlSerializer = new XmlSerializer(typeof(Level1), overrides);

第 1 步结果

命名空间已从 Level2 中删除,但未从 Level1 中删除。

第 2 步

添加了更多代码以尝试从 Level1 中删除命名空间。我尝试使用 XmlSerializer.Serialize() 方法的命名空间参数来使用空命名空间。注意“level1”是“Level1”类型的对象。

XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add("", "");

this.xmlSerializer.Serialize(this.xmlFileWriter, level1, namespaces);

第 2 步结果

命名空间已从 Level1 和 Level2 中删除。到现在为止还挺好!现在我需要做的就是将命名空间的东西添加到 RootLevel。

第 3 步

由于 RootLevel 没有序列化,我添加了一些 XmlWriter 代码来尝试将命名空间添加到 RootLevel。

string defaultNamespace = "http://www.MyCompany.com/MySchema.xsd";
this.xmlFileWriter.WriteStartElement("RootLevel", defaultNamespace);
this.xmlFileWriter.WriteAttributeString("xmlns", "xsi", "", "http://www.w3.org/2001/XMLSchema-instance");
this.xmlFileWriter.WriteAttributeString("xmlns", "xsd", "", "http://www.w3.org/2001/XMLSchema");

第 3 步结果

命名空间已添加到 RootLevel。耶!。 但是,现在每个 Level1 元素都有一个 xmlns="" 属性。呸!

<RootLevel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.MyCompany.com/MySchema.xsd">
    <Level1 xmlns="">
        <Level2>
        </Level2>
    </Level1 >
    <Level1 xmlns="">
        <Level2>
        </Level2>
    </Level1 >
    etc. repeats hundreds of times
</RootLevel>

那为什么会这样呢?

【问题讨论】:

【参考方案1】:

你没有发布你的原始代码,所以我不知道你到底哪里出错了,但我能够得到你想要的 XML,如下所示:

    提取一个静态帮助器类XmlNamespaces 来保存命名空间字符串并将它们声明为const 字符串。在您的问题中,您有时使用"http://MyCompany.com/MySchema.xsd",但有时使用"http://www.MyCompany.com/MySchema.xsd"。这可能只是问题中的一个错字,但如果您的代码不一致地使用这些,则会导致错误。

    除了XmlTypeAttribute之外,将XmlRootAttribute应用于Level1

    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = XmlNamespaces.Default)]
    [System.Xml.Serialization.XmlRootAttribute(Namespace = XmlNamespaces.Default, IsNullable = false)]
    public partial class Level1
    
        // Properties 
    
    

    写入根元素时,将默认命名空间字符串传递给XmlWriter.WriteStartElement(string, string)

整体代码如下:

public static class XmlNamespaces

    public const string Default = "http://MyCompany.com/MySchema.xsd";
    public const string xsi = "http://www.w3.org/2001/XMLSchema-instance";
    public const string xsd = "http://www.w3.org/2001/XMLSchema";

    public static XmlSerializerNamespaces XmlSerializerNamespaces
    
        get
        
            var namespaces = new XmlSerializerNamespaces();
            namespaces.Add("", XmlNamespaces.Default);
            namespaces.Add("xsi", XmlNamespaces.xsi);
            namespaces.Add("xsd", XmlNamespaces.xsd);
            return namespaces;
        
    


[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = XmlNamespaces.Default)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = XmlNamespaces.Default, IsNullable = false)] // Added this and used const string from XmlNamespaces class
public partial class Level1

    private List<Level2> level2Field;

    public List<Level2> Level2Field
    
        get  return this.level2Field; 
        set  this.level2Field = value; 
    

    /* other properties on Level1 go here*/


[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = XmlNamespaces.Default)] // Used const string  from XmlNamespaces class
public partial class Level2

    /* properties on Level2 go here*/


public static class XmlSerializationUtilities

    public static void WriteList<TItem>(string rootName, XmlSerializerNamespaces namespaces, IEnumerable<TItem> list, TextWriter textWriter)
    
        var namespaceList = namespaces.ToArray();
        string defaultNamespace = null;
        foreach (var ns in namespaceList)
        
            if (string.IsNullOrEmpty(ns.Name))
            
                defaultNamespace = ns.Namespace;
                break;
            
        

        var settings = new XmlWriterSettings();
        settings.Indent = true;
        settings.IndentChars = "    ";

        using (var writer = XmlWriter.Create(textWriter, settings))
        
            writer.WriteStartDocument();
            writer.WriteStartElement(rootName, defaultNamespace);

            foreach (var ns in namespaceList)
                if (!string.IsNullOrEmpty(ns.Name))
                    writer.WriteAttributeString("xmlns", ns.Name, null, ns.Namespace);

            var serializer = new XmlSerializer(typeof(TItem));
            foreach (var item in list)
            
                serializer.Serialize(writer, item);
            

            writer.WriteEndElement();
            writer.WriteEndDocument();
        
    


public static class TestClass

    public static void Test()
    
        var list = new List<Level1>  new Level1  Level2Field = new List<Level2>  new Level2(), new Level2()  , new Level1  Level2Field = new List<Level2>  new Level2(), new Level2(), new Level2(), new Level2()   ;

        string xml;

        using (var writer = new StringWriter())
        
            XmlSerializationUtilities.WriteList("RootLevel", XmlNamespaces.XmlSerializerNamespaces, list, writer);
            xml = writer.ToString();

            Debug.WriteLine(xml);
        
    

输出是

<?xml version="1.0" encoding="utf-16"?>
<RootLevel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://MyCompany.com/MySchema.xsd">
    <Level1>
        <Level2Field>
            <Level2 />
            <Level2 />
        </Level2Field>
    </Level1>
    <Level1>
        <Level2Field>
            <Level2 />
            <Level2 />
            <Level2 />
            <Level2 />
        </Level2Field>
    </Level1>
</RootLevel>

【讨论】:

好的,我编辑了我的原始帖子,所以它总是显示 www.MyCompany.com。 阅读您的帖子后,看来解决我的问题的关键是将 XmlRootAttribute 添加到 Level1 类。 我不知道我可以在我的 RootLevel 和 Level1 类上使用 XmlRootAttribute。通过这样做,我能够删除我试图覆盖命名空间内容的所有代码。

以上是关于序列化 xml 文件的一部分。想要根上的命名空间,而不是序列化的子元素的主要内容,如果未能解决你的问题,请参考以下文章

在不修改 C# XSD 类的情况下向 XML 序列化添加前缀和命名空间

具有命名空间的 XML 文档上的 XPath

XML 序列化和命名空间前缀

反序列化 xml,包括命名空间

Jackson XML - 使用命名空间前缀反序列化 XML

XML 序列化 + 命名空间 (C#)