XmlSerializer:删除不必要的xsi和xsd命名空间

Dav*_*nde 124 .net xml xml-serialization

有没有办法配置XmlSerializer,以便它不会在根元素中写入默认命名空间?

我得到的是这个:

<?xml ...>
<rootelement xmlns:xsi="..." xmlns:xsd="...">
</rootelement>
Run Code Online (Sandbox Code Playgroud)

我想删除两个xmlns声明.

重复:如何在不获取xmlns ="..."的情况下将对象序列化为XML?

Jer*_*emy 253

//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)
Run Code Online (Sandbox Code Playgroud)

  • 嗯......你们是叛徒.它在http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializernamespaces(v=vs.100).aspx中明确说明你不能这样做. (22认同)
  • 这个答案生成"xmlns:d1p1"和"xmlns:q1"命名空间.这是什么? (8认同)
  • 究竟.奇迹般有效. (4认同)
  • 我不确定为什么它"不受支持",但这正是我想要的. (3认同)
  • 好吧,这段代码适用于非常简单的序列化,没有其他命名空间定义.对于多个名称空间定义,工作答案是可接受的. (2认同)

fou*_*ght 60

由于Dave要求我在.NET中序列化对象时重复我省略所有xsi和xsd命名空间的答案,我已经更新了这篇文章并在上面的链接中重复了我的答案.此答案中使用的示例与用于其他问题的示例相同.以下内容是逐字复制的.


在线阅读了Microsoft的文档和几个解决方案之后,我发现了这个问题的解决方案.它适用于内置XmlSerializer和自定义XML序列化IXmlSerialiazble.

为了惠特,我将使用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;
}
Run Code Online (Sandbox Code Playgroud)

这就是这个课程的全部内容.现在,有些人反对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");

******/
XmlSerializer xs = new XmlSerializer(typeof(MyTypeWithNamespaces),
    new XmlRootAttribute("MyTypeWithNamespaces") { Namespace="urn:Abracadabra" });

// 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);
Run Code Online (Sandbox Code Playgroud)

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

<MyTypeWithNamespaces>
    <Label xmlns="urn:Whoohoo">myLabel</Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>
Run Code Online (Sandbox Code Playgroud)

我已经成功地使用这种方法,在最近的一个项目与那些有序列化到XML的Web服务调用类的深层次结构.微软的文档并不十分清楚,XmlSerializerNamespaces一旦你创建了这个可公开访问的成员,该怎么办,而且很多人认为它没用.但是按照他们的文档和上面显示的方式使用它,你可以自定义XmlSerializer的方式,而不诉诸于不支持的行为,或通过实施"滚动自己的"序列生成XML为你的类IXmlSerializable.

我希望这个答案能够一劳永逸地解决如何摆脱由此产生的标准xsixsd名称空间XmlSerializer.

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

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

现在,在我的类定义中,我指出<Label/>元素在命名空间中"urn:Whoohoo",所以我不需要做任何进一步的操作.当我现在使用上面的序列化代码序列化类时,这是输出:

<MyTypeWithNamespaces xmlns:w="urn:Whoohoo">
    <w:Label>myLabel</w:Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>
Run Code Online (Sandbox Code Playgroud)

因为<Label>是从文档的其余部分不同的命名空间,它必须,以某种方式,来"装饰"与命名空间.请注意,仍然没有xsixsd名称空间.


这结束了我对另一个问题的回答.但我想确保我回答OP关于不使用命名空间的问题,因为我觉得我还没有真正解决它.假设它<Label>是与文档其余部分相同的命名空间的一部分,在这种情况下urn:Abracadabra:

<MyTypeWithNamespaces>
    <Label>myLabel<Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>
Run Code Online (Sandbox Code Playgroud)

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

// 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;
Run Code Online (Sandbox Code Playgroud)

然后,在您使用该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);
Run Code Online (Sandbox Code Playgroud)

并且XmlSerializer会吐出与上面显示的相同的XML,输出中没有其他名称空间:

<MyTypeWithNamespaces>
    <Label>myLabel<Label>
    <Epoch>42</Epoch>
</MyTypeWithNamespaces>
Run Code Online (Sandbox Code Playgroud)

  • 我不明白,对于您最终的 OP 答案,您在序列化“urn:Abracadabra”(构造函数)期间仍在使用命名空间,为什么最终输出中未包含该命名空间。OP不应该使用:XmlSerializerNamespaces EmptyXmlSerializerNamespaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }); (2认同)
  • 这是正确的答案,尽管它不是投票最多的。对我不起作用的事情是`XmlTextWriter xtw =(XmlTextWriter)XmlTextWriter.Create(ms,xws);`我必须替换为`var xtw = XmlTextWriter.Create(memStm,xws);`。 (2认同)

Max*_*nce 8

我在用着:

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());
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

要获取以下 XML:

<?xml version="1.0"?>
<Person xmlns="http://www.something.org/schema">
  <FirstName>Donald</FirstName>
  <LastName>Duck</LastName>
</Person>
Run Code Online (Sandbox Code Playgroud)

如果您不需要命名空间,只需将 DEFAULT_NAMESPACE 设置为“”即可。

  • 如果我对一个已有 10 年历史的问题添加自己的答案,那是因为公认的答案比完整版的《圣经》要长。 (3认同)

Che*_*eso 6

还有一种方法 - 您可以在要序列化的类型中提供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;
}
Run Code Online (Sandbox Code Playgroud)

你可以这样做:

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");
Run Code Online (Sandbox Code Playgroud)

这意味着该实例的任何序列化都没有指定其自己的前缀+ URI对的集合,将使用"urn:mycompany.2009"命名空间的"p"前缀.它还将省略xsi和xsd命名空间.

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

  • 这会更多地污染课程.我的目标是不使用某些命名空间,我的目标是根本不使用命名空间. (2认同)