当前位置:  开发笔记 > 编程语言 > 正文

在.NET中序列化对象时省略所有xsi和xsd命名空间?

如何解决《在.NET中序列化对象时省略所有xsi和xsd命名空间?》经验,为你挑选了5个好方法。

代码如下所示:

StringBuilder builder = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
using (XmlWriter xmlWriter = XmlWriter.Create(builder, settings))
{
    XmlSerializer s = new XmlSerializer(objectToSerialize.GetType());
    s.Serialize(xmlWriter, objectToSerialize);
}

生成的序列化文档包含名称空间,如下所示:


 ...

要删除xsi和xsd命名空间,我可以按照如何将对象序列化为XML而不获取xmlns ="..."的答案?.

我希望我的消息标记为(没有任何命名空间属性).我怎样才能做到这一点?



1> Thomas Leves..:
...
XmlSerializer s = new XmlSerializer(objectToSerialize.GetType());
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("","");
s.Serialize(xmlWriter, objectToSerialize, ns);


可以简化为s.Serialize(writer,objectToSerialize,new XmlSerializerNamespaces(new [] {XmlQualifiedName.Empty}));`
此外,这不会删除*all*xml名称空间,如问题所示.它只删除了xsi和xsd命名空间,如问题http://stackoverflow.com/questions/258960中所述,该问题也在*this*问题中引用.
我只想补充一点,删除默认名称空间会产生意想不到的后果:例如,如果您使用XmlInclude属性序列化派生类型,则无论您是否需要,名称空间都会添加到每个这些元素中,因为它们是反序列化所必需的

2> Cheeso..:

这是两个答案中的第二个.

如果您想在序列化期间从文档中任意删除所有名称空间,可以通过实现自己的XmlWriter来实现.

最简单的方法是从XmlTextWriter派生并覆盖发出名称空间的StartElement方法.当发出包括根在内的任何元素时,XmlSerializer会调用StartElement方法.通过覆盖每个元素的命名空间,并用空字符串替换它,您已从输出中删除了命名空间.

public class NoNamespaceXmlWriter : XmlTextWriter
{
    //Provide as many contructors as you need
    public NoNamespaceXmlWriter(System.IO.TextWriter output)
        : base(output) { Formatting= System.Xml.Formatting.Indented;}

    public override void WriteStartDocument () { }

    public override void WriteStartElement(string prefix, string localName, string ns)
    {
        base.WriteStartElement("", localName, "");
    }
}

假设这是类型:

// explicitly specify a namespace for this type,
// to be used during XML serialization.
[XmlRoot(Namespace="urn:Abracadabra")]
public class MyTypeWithNamespaces
{
    // private fields backing the properties
    private int _Epoch;
    private string _Label;

    // explicitly define a distinct namespace for this element
    [XmlElement(Namespace="urn:Whoohoo")]
    public string Label
    {
        set {  _Label= value; } 
        get { return _Label; } 
    }

    // this property will be implicitly serialized to XML using the
    // member name for the element name, and inheriting the namespace from
    // the type.
    public int Epoch
    {
        set {  _Epoch= value; } 
        get { return _Epoch; } 
    }
}

以下是在序列化过程中如何使用这样的东西:

        var o2= new MyTypeWithNamespaces { ..intializers.. };
        var builder = new System.Text.StringBuilder();
        using ( XmlWriter writer = new NoNamespaceXmlWriter(new System.IO.StringWriter(builder)))
        {
            s2.Serialize(writer, o2, ns2);
        }            
        Console.WriteLine("{0}",builder.ToString());

但是,XmlTextWriter有点破碎.根据参考文档,当它写入时,它不会检查以下内容:

属性和元素名称中的字符无效.

不符合指定编码的Unicode字符.如果Unicode字符不符合指定的编码,则XmlTextWriter不会将Unicode字符转义为字符实体.

重复的属性.

DOCTYPE公共标识符或系统标识符中的字符.

XmlTextWriter的这些问题自.NET Framework的v1.1开始就存在,并且它们将保留,以实现向后兼容性.如果您对这些问题没有任何顾虑,那么请务必使用XmlTextWriter.但大多数人都希望获得更高的可靠性.

为了实现这一点,在序列化期间仍然抑制名称空间,而不是从XmlTextWriter派生,定义抽象XmlWriter及其24种方法的具体实现.

这里有一个例子:

public class XmlWriterWrapper : XmlWriter
{
    protected XmlWriter writer;

    public XmlWriterWrapper(XmlWriter baseWriter)
    {
        this.Writer = baseWriter;
    }

    public override void Close()
    {
        this.writer.Close();
    }

    protected override void Dispose(bool disposing)
    {
        ((IDisposable) this.writer).Dispose();
    }

    public override void Flush()
    {
        this.writer.Flush();
    }

    public override string LookupPrefix(string ns)
    {
        return this.writer.LookupPrefix(ns);
    }

    public override void WriteBase64(byte[] buffer, int index, int count)
    {
        this.writer.WriteBase64(buffer, index, count);
    }

    public override void WriteCData(string text)
    {
        this.writer.WriteCData(text);
    }

    public override void WriteCharEntity(char ch)
    {
        this.writer.WriteCharEntity(ch);
    }

    public override void WriteChars(char[] buffer, int index, int count)
    {
        this.writer.WriteChars(buffer, index, count);
    }

    public override void WriteComment(string text)
    {
        this.writer.WriteComment(text);
    }

    public override void WriteDocType(string name, string pubid, string sysid, string subset)
    {
        this.writer.WriteDocType(name, pubid, sysid, subset);
    }

    public override void WriteEndAttribute()
    {
        this.writer.WriteEndAttribute();
    }

    public override void WriteEndDocument()
    {
        this.writer.WriteEndDocument();
    }

    public override void WriteEndElement()
    {
        this.writer.WriteEndElement();
    }

    public override void WriteEntityRef(string name)
    {
        this.writer.WriteEntityRef(name);
    }

    public override void WriteFullEndElement()
    {
        this.writer.WriteFullEndElement();
    }

    public override void WriteProcessingInstruction(string name, string text)
    {
        this.writer.WriteProcessingInstruction(name, text);
    }

    public override void WriteRaw(string data)
    {
        this.writer.WriteRaw(data);
    }

    public override void WriteRaw(char[] buffer, int index, int count)
    {
        this.writer.WriteRaw(buffer, index, count);
    }

    public override void WriteStartAttribute(string prefix, string localName, string ns)
    {
        this.writer.WriteStartAttribute(prefix, localName, ns);
    }

    public override void WriteStartDocument()
    {
        this.writer.WriteStartDocument();
    }

    public override void WriteStartDocument(bool standalone)
    {
        this.writer.WriteStartDocument(standalone);
    }

    public override void WriteStartElement(string prefix, string localName, string ns)
    {
        this.writer.WriteStartElement(prefix, localName, ns);
    }

    public override void WriteString(string text)
    {
        this.writer.WriteString(text);
    }

    public override void WriteSurrogateCharEntity(char lowChar, char highChar)
    {
        this.writer.WriteSurrogateCharEntity(lowChar, highChar);
    }

    public override void WriteValue(bool value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(DateTime value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(decimal value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(double value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(int value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(long value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(object value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(float value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteValue(string value)
    {
        this.writer.WriteValue(value);
    }

    public override void WriteWhitespace(string ws)
    {
        this.writer.WriteWhitespace(ws);
    }


    public override XmlWriterSettings Settings
    {
        get
        {
            return this.writer.Settings;
        }
    }

    protected XmlWriter Writer
    {
        get
        {
            return this.writer;
        }
        set
        {
            this.writer = value;
        }
    }

    public override System.Xml.WriteState WriteState
    {
        get
        {
            return this.writer.WriteState;
        }
    }

    public override string XmlLang
    {
        get
        {
            return this.writer.XmlLang;
        }
    }

    public override System.Xml.XmlSpace XmlSpace
    {
        get
        {
            return this.writer.XmlSpace;
        }
    }        
}

然后,提供一个覆盖StartElement方法的派生类,如前所述:

public class NamespaceSupressingXmlWriter : XmlWriterWrapper
{
    //Provide as many contructors as you need
    public NamespaceSupressingXmlWriter(System.IO.TextWriter output)
        : base(XmlWriter.Create(output)) { }

    public NamespaceSupressingXmlWriter(XmlWriter output)
        : base(XmlWriter.Create(output)) { }

    public override void WriteStartElement(string prefix, string localName, string ns)
    {
        base.WriteStartElement("", localName, "");
    }
}

然后像这样使用这个作者:

        var o2= new MyTypeWithNamespaces { ..intializers.. };
        var builder = new System.Text.StringBuilder();
        var settings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent= true };
        using ( XmlWriter innerWriter = XmlWriter.Create(builder, settings))
            using ( XmlWriter writer = new NamespaceSupressingXmlWriter(innerWriter))
            {
                s2.Serialize(writer, o2, ns2);
            }            
        Console.WriteLine("{0}",builder.ToString());

感谢Oleg Tkachenko.


我发现我还需要覆盖`LookupPrefix(string ns)`以始终返回空字符串以删除所有模式声明.

3> fourpastmidn..:

在线阅读了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;
}

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

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


    
    42

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

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

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

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

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


    myLabel
    42

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



4> Cheeso..:

这是我对这个问题的两个答案中的第一个.

如果要对命名空间进行精细控制 - 例如,如果要省略其中一些命名空间而不想删除其他命名空间,或者如果要将一个命名空间替换为另一个命名空间,则可以使用XmlAttributeOverrides执行此操作.

假设您有这种类型定义:

// explicitly specify a namespace for this type,
// to be used during XML serialization.
[XmlRoot(Namespace="urn:Abracadabra")]
public class MyTypeWithNamespaces
{
    // private fields backing the properties
    private int _Epoch;
    private string _Label;

    // explicitly define a distinct namespace for this element
    [XmlElement(Namespace="urn:Whoohoo")]
    public string Label
    {
        set {  _Label= value; } 
        get { return _Label; } 
    }

    // this property will be implicitly serialized to XML using the
    // member name for the element name, and inheriting the namespace from
    // the type.
    public int Epoch
    {
        set {  _Epoch= value; } 
        get { return _Epoch; } 
    }
}

而这个序列化伪代码:

        var o2= new MyTypeWithNamespaces() { ..initializers...};
        ns.Add( "", "urn:Abracadabra" );
        XmlSerializer s2 = new XmlSerializer(typeof(MyTypeWithNamespaces));
        s2.Serialize(System.Console.Out, o2, ns);

你会得到类似这样的XML:


  
  97

请注意,根元素上有一个默认命名空间,"Label"元素上还有一个不同的命名空间.这些名称空间由上面代码中装饰类型的属性决定.

.NET中的Xml序列化框架包括显式覆盖装饰实际代码的属性的可能性.您可以使用XmlAttributesOverrides类和朋友执行此操作.假设我有相同的类型,我这样序列化:

        // instantiate the container for all attribute overrides
        XmlAttributeOverrides xOver = new XmlAttributeOverrides();

        // define a set of XML attributes to apply to the root element
        XmlAttributes xAttrs1 = new XmlAttributes();

        // define an XmlRoot element (as if [XmlRoot] had decorated the type)
        // The namespace in the attribute override is the empty string. 
        XmlRootAttribute xRoot = new XmlRootAttribute() { Namespace = ""};

        // add that XmlRoot element to the container of attributes
        xAttrs1.XmlRoot= xRoot;

        // add that bunch of attributes to the container holding all overrides
        xOver.Add(typeof(MyTypeWithNamespaces), xAttrs1);

        // create another set of XML Attributes
        XmlAttributes xAttrs2 = new XmlAttributes();

        // define an XmlElement attribute, for a type of "String", with no namespace
        var xElt = new XmlElementAttribute(typeof(String)) { Namespace = ""};

        // add that XmlElement attribute to the 2nd bunch of attributes
        xAttrs2.XmlElements.Add(xElt);

        // add that bunch of attributes to the container for the type, and
        // specifically apply that bunch to the "Label" property on the type.
        xOver.Add(typeof(MyTypeWithNamespaces), "Label", xAttrs2);

        // instantiate a serializer with the overrides 
        XmlSerializer s3 = new XmlSerializer(typeof(MyTypeWithNamespaces), xOver);

        // serialize
        s3.Serialize(System.Console.Out, o2, ns2);

结果看起来像这样;


  
  97

您已剥离名称空间.

一个逻辑问题是,是否可以在序列化期间从任意类型中删除所有名称空间,而无需通过显式覆盖? 答案是肯定的,如何做到这一点在我的下一个回复中.



5> Tejas..:
XmlSerializer sr = new XmlSerializer(objectToSerialize.GetType());
TextWriter xmlWriter = new StreamWriter(filename);
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
sr.Serialize(xmlWriter, objectToSerialize, namespaces);

推荐阅读
个性2402852463
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有