【问题标题】:XmlSerializer: remove unnecessary xsi and xsd namespacesXmlSerializer:删除不必要的 xsi 和 xsd 命名空间
【发布时间】:2010-10-20 02:29:04
【问题描述】:

有没有办法配置 XmlSerializer 使其不在根元素中写入默认命名空间?

我得到的是这样的:

<?xml ...>
<rootelement xmlns:xsi="..." xmlns:xsd="...">
</rootelement>

我想删除两个 xmlns 声明。

重复How to serialize an object to XML without getting xmlns=”…”?

【问题讨论】:

    标签: .net xml xml-serialization


    【解决方案1】:
    //Create our own namespaces for the output
    XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
    
    //Add an empty namespace and empty value
    ns.Add("", "");
    
    //Create the serializer
    XmlSerializer slz = new XmlSerializer(someType);
    
    //Serialize the object with our own namespaces (notice the overload)
    slz.Serialize(myXmlTextWriter, someObject, ns)
    

    【讨论】:

    • 嗯……你们是叛逆者。它在msdn.microsoft.com/en-us/library/… 明确表示您不能这样做。
    • 布尔耶! (为了克服女士所说的你不能做的事情)
    • 我不确定它为什么“不受支持”,但这正是我想要的。
    • 这个答案生成“xmlns:d1p1”和“xmlns:q1”命名空间。这是什么?
    • 好吧,这段代码适用于非常简单的序列化,没有其他命名空间定义。对于多个命名空间定义,有效的答案是公认的。
    【解决方案2】:

    还有另一种选择——您可以在要序列化的类型中提供XmlSerializerNamespaces 类型的成员。用XmlNamespaceDeclarations 属性装饰它。将命名空间前缀和 URI 添加到该成员。然后,任何未显式提供 XmlSerializerNamespaces 的序列化都将使用您放入类型中的命名空间前缀+URI 对。

    示例代码,假设这是您的类型:

    [XmlRoot(Namespace = "urn:mycompany.2009")]
    public class Person {
      [XmlAttribute] 
      public bool Known;
      [XmlElement]
      public string Name;
      [XmlNamespaceDeclarations]
      public XmlSerializerNamespaces xmlns;
    }
    

    你可以这样做:

    var p = new Person
      { 
          Name = "Charley",
          Known = false, 
          xmlns = new XmlSerializerNamespaces()
      }
    p.xmlns.Add("",""); // default namespace is emoty
    p.xmlns.Add("c", "urn:mycompany.2009");
    

    这意味着该实例的任何未指定其自己的一组前缀+URI 对的序列化都将使用“urn:mycompany.2009”命名空间的“p”前缀。它还将省略 xsi 和 xsd 命名空间。

    这里的区别在于您将 XmlSerializerNamespaces 添加到类型本身,而不是在调用 XmlSerializer.Serialize() 时显式使用它。这意味着,如果您的类型的实例由您不拥有的代码序列化(例如在 Web 服务堆栈中),并且该代码未显式提供 XmlSerializerNamespaces,则该序列化程序将使用实例中提供的命名空间。

    【讨论】:

    • 1.我看不出有什么区别。您仍在将默认命名空间添加到 XmlSerializerNamespaces 的实例中。
    • 2.这更加污染了课堂。我的目标是不使用某些命名空间,我的目标是根本不使用命名空间。
    • 我添加了一条说明,说明此方法与仅在序列化期间指定 XmlSerializerNamespaces 的方法之间的区别。
    【解决方案3】:

    由于 Dave 要求我重复我对 Omitting all xsi and xsd namespaces when serializing an object in .NET 的回答,我已经更新了这篇文章,并从上述链接中重复了我的回答。此答案中使用的示例与其他问题中使用的示例相同。以下内容逐字复制。


    在网上阅读了微软的文档和几个解决方案后,我发现了这个问题的解决方案。它适用于内置的XmlSerializer 和通过IXmlSerialiazble 的自定义XML 序列化。

    顺便说一句,我将使用迄今为止在此问题的答案中使用的相同 MyTypeWithNamespaces XML 示例。

    [XmlRoot("MyTypeWithNamespaces", Namespace="urn:Abracadabra", IsNullable=false)]
    public class MyTypeWithNamespaces
    {
        // As noted below, per Microsoft's documentation, if the class exposes a public
        // member of type XmlSerializerNamespaces decorated with the 
        // XmlNamespacesDeclarationAttribute, then the XmlSerializer will utilize those
        // namespaces during serialization.
        public MyTypeWithNamespaces( )
        {
            this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
                // Don't do this!! Microsoft's documentation explicitly says it's not supported.
                // It doesn't throw any exceptions, but in my testing, it didn't always work.
    
                // new XmlQualifiedName(string.Empty, string.Empty),  // And don't do this:
                // new XmlQualifiedName("", "")
    
                // DO THIS:
                new XmlQualifiedName(string.Empty, "urn:Abracadabra") // Default Namespace
                // Add any other namespaces, with prefixes, here.
            });
        }
    
        // If you have other constructors, make sure to call the default constructor.
        public MyTypeWithNamespaces(string label, int epoch) : this( )
        {
            this._label = label;
            this._epoch = epoch;
        }
    
        // An element with a declared namespace different than the namespace
        // of the enclosing type.
        [XmlElement(Namespace="urn:Whoohoo")]
        public string Label
        {
            get { return this._label; }
            set { this._label = value; }
        }
        private string _label;
    
        // An element whose tag will be the same name as the property name.
        // Also, this element will inherit the namespace of the enclosing type.
        public int Epoch
        {
            get { return this._epoch; }
            set { this._epoch = value; }
        }
        private int _epoch;
    
        // Per Microsoft's documentation, you can add some public member that
        // returns a XmlSerializerNamespaces object. They use a public field,
        // but that's sloppy. So I'll use a private backed-field with a public
        // getter property. Also, per the documentation, for this to work with
        // the XmlSerializer, decorate it with the XmlNamespaceDeclarations
        // attribute.
        [XmlNamespaceDeclarations]
        public XmlSerializerNamespaces Namespaces
        {
            get { return this._namespaces; }
        }
        private XmlSerializerNamespaces _namespaces;
    }
    

    这就是这门课的全部内容。现在,有些人反对在他们的类中的某处有一个XmlSerializerNamespaces 对象;但正如您所见,我巧妙地将其隐藏在默认构造函数中,并公开了一个公共属性以返回命名空间。

    现在,当需要序列化类时,您将使用以下代码:

    MyTypeWithNamespaces myType = new MyTypeWithNamespaces("myLabel", 42);
    
    /******
       OK, I just figured I could do this to make the code shorter, so I commented out the
       below and replaced it with what follows:
    
    // You have to use this constructor in order for the root element to have the right namespaces.
    // If you need to do custom serialization of inner objects, you can use a shortened constructor.
    XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces), new XmlAttributeOverrides(),
        new Type[]{}, new XmlRootAttribute("MyTypeWithNamespaces"), "urn:Abracadabra");
    
    ******/
    
    /*****
      Per @dbc, since MyTypeWithNamespaces has a XmlRootAttribute decorating the class,
      You may be able to get away with NOT using this .ctor and use the simple
      XmlSerializer(Type) .ctor.
      Also, be careful not to use serializer creation in loops, as it could lead
      to extensive memory issues due to how serializers are cached (or not...).
      See @dbc's comment and link to SO Q&A for more details.
    
    XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces),
        new XmlRootAttribute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" });
    ****/
    XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces));
    
    // I'll use a MemoryStream as my backing store.
    MemoryStream ms = new MemoryStream();
    
    // This is extra! If you want to change the settings for the XmlSerializer, you have to create
    // a separate XmlWriterSettings object and use the XmlTextWriter.Create(...) factory method.
    // So, in this case, I want to omit the XML declaration.
    XmlWriterSettings xws = new XmlWriterSettings();
    xws.OmitXmlDeclaration = true;
    xws.Encoding = Encoding.UTF8; // This is probably the default
    // You could use the XmlWriterSetting to set indenting and new line options, but the
    // XmlTextWriter class has a much easier method to accomplish that.
    
    // The factory method returns a XmlWriter, not a XmlTextWriter, so cast it.
    XmlTextWriter xtw = (XmlTextWriter)XmlTextWriter.Create(ms, xws);
    // Then we can set our indenting options (this is, of course, optional).
    xtw.Formatting = Formatting.Indented;
    
    // Now serialize our object.
    xs.Serialize(xtw, myType, myType.Namespaces);
    

    完成此操作后,您应该得到以下输出:

    <MyTypeWithNamespaces>
        <Label xmlns="urn:Whoohoo">myLabel</Label>
        <Epoch>42</Epoch>
    </MyTypeWithNamespaces>
    

    我在最近的一个项目中成功地使用了这种方法,其中包含一系列被序列化为 XML 以进行 Web 服务调用的类的深层层次。微软的文档并不太清楚如何处理可公开访问的XmlSerializerNamespaces 成员,一旦你创建了它,很多人认为它没用。但是通过遵循他们的文档并以上面显示的方式使用它,您可以自定义 XmlSerializer 如何为您的类生成 XML,而无需诉诸不受支持的行为或通过实现 IXmlSerializable“滚动您自己的”序列化。

    我希望这个答案能够彻底解决如何摆脱由XmlSerializer 生成的标准xsixsd 命名空间。

    更新:我只是想确保我回答了 OP 关于删除所有命名空间的问题。我上面的代码将为此工作;让我告诉你怎么做。现在,在上面的示例中,您确实无法摆脱所有名称空间(因为有两个名称空间在使用中)。在您的 XML 文档中的某处,您将需要有类似 xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo 的内容。如果示例中的类是较大文档的一部分,则必须为AbracadbraWhoohoo 之一(或两者)声明命名空间上方的某个位置。如果没有,那么一个或两个命名空间中的元素必须用某种前缀修饰(你不能有两个默认命名空间,对吧?)。因此,对于此示例,Abracadabra 是默认命名空间。我可以在 MyTypeWithNamespaces 类中为 Whoohoo 命名空间添加命名空间前缀,如下所示:

    public MyTypeWithNamespaces
    {
        this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
            new XmlQualifiedName(string.Empty, "urn:Abracadabra"), // Default Namespace
            new XmlQualifiedName("w", "urn:Whoohoo")
        });
    }
    

    现在,在我的类定义中,我指出&lt;Label/&gt; 元素在命名空间"urn:Whoohoo" 中,所以我不需要做任何进一步的事情。当我现在使用上面的序列化代码来序列化类时,输出如下:

    <MyTypeWithNamespaces xmlns:w="urn:Whoohoo">
        <w:Label>myLabel</w:Label>
        <Epoch>42</Epoch>
    </MyTypeWithNamespaces>
    

    因为&lt;Label&gt; 与文档的其余部分位于不同的命名空间中,所以它必须以某种方式使用命名空间“装饰”。请注意,仍然没有 xsixsd 命名空间。


    这结束了我对另一个问题的回答。但我想确保我回答了 OP 关于不使用名称空间的问题,因为我觉得我还没有真正解决它。假设&lt;Label&gt; 与文档的其余部分属于同一命名空间,在本例中为urn:Abracadabra

    <MyTypeWithNamespaces>
        <Label>myLabel<Label>
        <Epoch>42</Epoch>
    </MyTypeWithNamespaces>
    

    您的构造函数看起来就像我在第一个代码示例中的样子,以及用于检索默认命名空间的公共属性:

    // As noted below, per Microsoft's documentation, if the class exposes a public
    // member of type XmlSerializerNamespaces decorated with the 
    // XmlNamespacesDeclarationAttribute, then the XmlSerializer will utilize those
    // namespaces during serialization.
    public MyTypeWithNamespaces( )
    {
        this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
            new XmlQualifiedName(string.Empty, "urn:Abracadabra") // Default Namespace
        });
    }
    
    [XmlNamespaceDeclarations]
    public XmlSerializerNamespaces Namespaces
    {
        get { return this._namespaces; }
    }
    private XmlSerializerNamespaces _namespaces;
    

    然后,稍后,在您使用MyTypeWithNamespaces 对象对其进行序列化的代码中,您可以像我上面那样调用它:

    MyTypeWithNamespaces myType = new MyTypeWithNamespaces("myLabel", 42);
    
    XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces),
        new XmlRootAttribute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" });
    
    ...
    
    // Above, you'd setup your XmlTextWriter.
    
    // Now serialize our object.
    xs.Serialize(xtw, myType, myType.Namespaces);
    

    XmlSerializer 会吐出与上图相同的 XML,输出中没有额外的命名空间:

    <MyTypeWithNamespaces>
        <Label>myLabel<Label>
        <Epoch>42</Epoch>
    </MyTypeWithNamespaces>
    

    【讨论】:

    • 再次来到这里检查这个,因为这是我找到的最直接的解释。谢谢@fourpastmidnight
    • 我不明白,对于您最终 OP 的回答,您在序列化“urn:Abracadabra”(构造函数)期间仍在使用命名空间,为什么最终输出中不包含它。 OP 不应该使用: XmlSerializerNamespaces EmptyXmlSerializerNamespaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
    • 这是正确的答案,虽然不是投票最多的。对我不起作用的是 XmlTextWriter xtw = (XmlTextWriter)XmlTextWriter.Create(ms, xws); 我不得不用 var xtw = XmlTextWriter.Create(memStm, xws); 替换。
    • 我已经有一段时间没有写这个答案了。 XmlTextWriter.Create 返回一个(抽象的?)XmlWriter 实例。所以@Preza8 是正确的,你将失去设置其他XmlTextWriter 特定属性的能力(至少,不是没有向下转换它),因此,特定转换为XmlTextWriter
    • @dbc 我更新了一个代码示例,该示例为使用XmlRootAttribute 装饰的类创建了一个序列化程序。 OTOH,后一个示例,虽然具有相同的类名,但 NOT 装饰有 XmlRootAttribute,因此我保留了较长的 .ctor 调用,因为在该实例中需要它。无论如何,人们应该知道他们执行序列化的上下文并设计一个合适的解决方案。作为一次性示例,上面的代码通常很好。再次感谢您的见解!
    【解决方案4】:

    我正在使用:

    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            const string DEFAULT_NAMESPACE = "http://www.something.org/schema";
            var serializer = new XmlSerializer(typeof(Person), DEFAULT_NAMESPACE);
            var namespaces = new XmlSerializerNamespaces();
            namespaces.Add("", DEFAULT_NAMESPACE);
    
            using (var stream = new MemoryStream())
            {
                var someone = new Person
                {
                    FirstName = "Donald",
                    LastName = "Duck"
                };
                serializer.Serialize(stream, someone, namespaces);
                stream.Position = 0;
                using (var reader = new StreamReader(stream))
                {
                    Console.WriteLine(reader.ReadToEnd());
                }
            }
        }
    }
    

    获取以下 XML:

    <?xml version="1.0"?>
    <Person xmlns="http://www.something.org/schema">
      <FirstName>Donald</FirstName>
      <LastName>Duck</LastName>
    </Person>
    

    如果您不想要命名空间,只需将 DEFAULT_NAMESPACE 设置为 ""。

    【讨论】:

    • 虽然这个问题已经有 10 多年的历史了,但当时的重点是要有一个根本不包含任何命名空间声明的 XML 正文。
    • 如果我将自己的答案添加到一个 10 年前的问题,那是因为接受的答案比完整版的圣经读起来要长。
    • 投票最多的答案提倡一种不推荐的方法(空命名空间)。
    • 我无能为力。我只能将接受的答案设为我认为最正确的答案。
    【解决方案5】:

    我知道它很脏,但它对我有用,只是使用正则表达式来摆脱垃圾。我只是不想要任何 xmlns 的东西,想要像普通 JSON 一样对待 XML。其他的回答太仪式感了。

    所以在序列化对象之后,我会这样做:

    string xml = "<string xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">Hello, world!</string>";
    xml = Regex.Replace(x, @" xmlns:.*?"".*?""", "");
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-12
      相关资源
      最近更新 更多