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

代码如下所示:

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); } 

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

 <message xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns="urn:something"> ... </message> 

要除去xsi和xsd命名空间,我可以按照如何将对象序列化到XML而不使用xmlns =“…”的答案? 。

我希望我的消息标记为<message> (没有任何名称空间属性)。 我该怎么做?

 ... XmlSerializer s = new XmlSerializer(objectToSerialize.GetType()); XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("",""); s.Serialize(xmlWriter, objectToSerialize, ns); 

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

如果要在序列化过程中从文档中任意抽取所有名称空间,可以通过实现自己的XmlWriter来完成。

最简单的方法是从XmlTextWriter派生并重写发出命名空间的StartElement方法。 StartElement方法在发送任何元素(包括根)时由XmlSerializer调用。 通过覆盖每个元素的名称空间,并用空stringreplace它,您已经从输出中剥离了名称空间。

 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, ""); } } 

假设这是types:

 // 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 。

在网上阅读了微软的文档和几个解决scheme之后,我发现了解决这个问题的办法。 它可以通过IXmlSerialiazble与内置的XmlSerializer和自定义XML序列化配合使用。

MyTypeWithNamespaces ,我将使用迄今为止在这个问题的答案中使用的相同的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; } 

这就是这个class。 现在,有人反对在他们的类中有一个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); 

一旦你完成了这个,你应该得到以下输出:

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

我已经在最近的一个项目中成功地使用了这个方法,这个项目有一个深层次的类,这些类被序列化为XML以用于Web服务调用。 微软的文档并不是很清楚如何处理公共可访问的XmlSerializerNamespaces成员一旦创build它,​​许多人认为这是无用的。 但通过遵循他们的文档并以上面所示的方式使用它,您可以自定义XmlSerializer如何为您的类生成XML,而无需采取不支持的行为,或通过实现IXmlSerializable “滚动自己的”序列化。

我希望这个答案能够xsi摆脱XmlSerializer生成的标准xsixsd命名空间。

更新:我只是想确保我回答了关于删除所有命名空间的OP的问题。 我上面的代码将为此工作; 让我告诉你如何。 现在,在上面的例子中,你真的不能摆脱所有的命名空间(因为有两个使用的命名空间)。 在你的XML文档的某个地方,你将需要像xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo 。如果示例中的类是大文档的一部分,那么在命名空间上方必须为AbracadbraWhoohoo任何一个(或两者) AbracadbraWhoohoo ,一个或两个命名空间中的元素必须使用某种types的前缀进行修饰(不能有两个默认的命名空间,对不对?)因此,对于这个例子, Abracadabra是defalt命名空间,我可以在我的MyTypeWithNamespaces类中为Whoohoo命名空间添加一个命名空间前缀,如下Whoohoo

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

现在,在我的类定义中,我指出<Label/>元素在名称空间"urn:Whoohoo" ,所以我不需要做任何事情。 当我现在使用我的上面的序列化代码序列化类,这是输出:

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

因为<Label>与文档的其余部分位于不同的名称空间中,所以它必须在某种程度上用命名空间“装饰”。 请注意,仍然没有xsixsd命名空间。

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

如果你想对命名空间进行很好的控制 – 例如,如果你想省略其中的一些命名空间,或者如果你想用另一个命名空间replace,你可以使用XmlAttributeOverrides来完成 。

假设你有这个types定义:

 // 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:

 <MyTypeWithNamespaces xmlns="urn:Abracadabra"> <Label xmlns="urn:Whoohoo">Cimsswybclaeqjh</Label> <Epoch>97</Epoch> </MyTypeWithNamespaces> 

请注意,根元素上有一个默认名称空间,“Label”元素上也有一个不同的名称空间。 在上面的代码中,这些命名空间是由装饰types的属性决定的。

.NET中的Xml序列化框架包含显式重写装饰实际代码的属性的可能性。 你用XmlAttributesOverrides类和朋友做到这一点。 假设我有相同的types,并且用这种方式来序列化它:

  // 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); 

结果是这样的;

 <MyTypeWithNamespaces> <Label>Cimsswybclaeqjh</Label> <Epoch>97</Epoch> </MyTypeWithNamespaces> 

你已经剥离了命名空间。

一个合乎逻辑的问题是, 可以在序列化期间从任意types剥离所有名称空间,而不必经过显式覆盖。 答案是肯定的,如何做是在我的下一个回应。

 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);