Omitindo todos os namespaces xsi e xsd ao serializar um object no .NET?

O código é assim:

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

O documento serializado resultante inclui namespaces, assim:

  ...  

Para remover os namespaces xsi e xsd, posso seguir a resposta de Como serializar um object para XML sem obter xmlns = ”…”? .

Eu quero minha tag de mensagem como (sem quaisquer atributos de namespace). Como posso fazer isso?

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

Esta é a segunda das duas respostas.

Se você quiser apenas remover todos os namespaces arbitrariamente de um documento durante a serialização, você pode fazer isso implementando seu próprio XmlWriter.

A maneira mais fácil é derivar de XmlTextWriter e replace o método StartElement que emite namespaces. O método StartElement é invocado pelo XmlSerializer ao emitir todos os elementos, incluindo a raiz. Ao replace o namespace de cada elemento e substituí-lo pela string vazia, você removeu os namespaces da saída.

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

Suponha que este seja o tipo:

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

Veja como você usaria essa coisa durante a serialização:

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

O XmlTextWriter é meio que quebrado, no entanto. De acordo com o documento de referência , quando ele é gravado, ele não verifica o seguinte:

  • Caracteres inválidos nos nomes de atributos e elementos.

  • Caracteres Unicode que não se ajustam à codificação especificada. Se os caracteres Unicode não se encheckboxm na codificação especificada, o XmlTextWriter não escapa os caracteres Unicode em entidades de caractere.

  • Atributos duplicados.

  • Caracteres no identificador público DOCTYPE ou no identificador do sistema.

Esses problemas com XmlTextWriter existem desde a v1.1 do .NET Framework, e eles permanecerão, para compatibilidade com versões anteriores. Se você não tem nenhuma preocupação sobre esses problemas, use o XmlTextWriter. Mas a maioria das pessoas gostaria de ter um pouco mais de confiabilidade.

Para obter isso, embora ainda suprima namespaces durante a serialização, em vez de derivar de XmlTextWriter, defina uma implementação concreta do XmlWriter abstrato e seus 24 methods.

Um exemplo está aqui:

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

Em seguida, forneça uma class derivada que substitua o método StartElement, como antes:

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

E então use este escritor assim:

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

Crédito para isso para Oleg Tkachenko .

Depois de ler a documentação da Microsoft e várias soluções on-line, descobri a solução para esse problema. Ele funciona com o XmlSerializer e com a serialização XML personalizada via IXmlSerialiazble .

A MyTypeWithNamespaces usarei a mesma amostra XML MyTypeWithNamespaces que foi usada nas respostas desta pergunta até o momento.

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

Isso é tudo para essa class. Agora, alguns se opuseram a ter um object XmlSerializerNamespaces algum lugar dentro de suas classs; mas como você pode ver, limpei-o no construtor padrão e expus uma propriedade pública para retornar os namespaces.

Agora, quando chegar a hora de serializar a class, você usaria o seguinte código:

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

Depois de ter feito isso, você deve obter a seguinte saída:

   42  

Eu usei com êxito este método em um projeto recente com uma hierarquia profunda de classs que são serializadas para XML para chamadas de serviço da web. A documentação da Microsoft não é muito clara sobre o que fazer com o membro publicamente acessível XmlSerializerNamespaces depois de criá-lo, e muitos acham que é inútil. Mas seguindo sua documentação e usando-o da maneira mostrada acima, você pode personalizar como o XmlSerializer gera XML para suas classs sem recorrer ao comportamento sem suporte ou serialização “rolando seu próprio” implementando IXmlSerializable .

É minha esperança que essa resposta ponha de lado, de uma vez por todas, como se livrar dos namespaces xsi e xsd padrão gerados pelo XmlSerializer .

ATUALIZAÇÃO: Eu só quero ter certeza de que respondi a pergunta do OP sobre a remoção de todos os namespaces. Meu código acima funcionará para isso; Deixa-me mostrar-te como. Agora, no exemplo acima, você realmente não pode se livrar de todos os namespaces (porque há dois namespaces em uso). Em algum lugar no seu documento XML, você precisará ter algo como xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo . Se a class no exemplo fizer parte de um documento maior, então em algum lugar acima de um namespace deve ser declarado para qualquer um dos (ou ambos) Abracadbra e Whoohoo não, então o elemento em um ou ambos os namespaces deve ser decorado com um prefixo de algum tipo (você não pode ter dois namespaces padrão, certo?) Então, para este exemplo, Abracadabra é o namespace MyTypeWithNamespaces poderia dentro da minha class MyTypeWithNamespaces adicionar um prefixo de namespace para o namespace Whoohoo da Whoohoo forma:

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

Agora, na minha definição de class, indiquei que o elemento está no namespace "urn:Whoohoo" , então não preciso fazer mais nada. Quando eu agora serializar a class usando o código de serialização acima inalterado, esta é a saída:

  myLabel 42  

Como está em um namespace diferente do restante do documento, ele deve, de alguma forma, ser “decorado” com um namespace. Observe que ainda não há namespaces xsi e xsd .

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

Esta é a primeira das minhas duas respostas à pergunta.

Se você quiser um controle preciso sobre os namespaces – por exemplo, se quiser omitir alguns deles, mas não outros, ou se quiser replace um namespace por outro, faça isso usando XmlAttributeOverrides .

Suponha que você tenha essa definição de tipo:

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

E esse pseudo-código de serialização:

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

Você obteria algo parecido com este XML:

   97  

Observe que há um namespace padrão no elemento raiz e também há um namespace distinto no elemento “Label”. Esses namespaces foram ditados pelos atributos que decoram o tipo, no código acima.

A estrutura Xml Serialization no .NET inclui a possibilidade de replace explicitamente os atributos que decoram o código real. Você faz isso com a class XmlAttributesOverrides e amigos. Suponha que eu tenha o mesmo tipo e serialize-o desta maneira:

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

O resultado é assim;

   97  

Você removeu os namespaces.

Uma questão lógica é: você pode remover todos os namespaces de tipos arbitrários durante a serialização, sem passar pelas substituições explícitas? A resposta é sim, e como fazer isso é na minha próxima resposta.