Pominięcie wszystkich przestrzeni nazw xsi i xsd podczas serializacji obiektu w .NET?

134

Kod wygląda następująco:

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

Wynikowy dokument serializowany zawiera przestrzenie nazw, na przykład:

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

Aby usunąć przestrzenie nazw xsi i xsd, mogę postępować zgodnie z odpowiedzią z artykułu Jak serializować obiekt do XML bez uzyskiwania xmlns = ”…”? .

Chcę, aby mój znacznik wiadomości był <message>(bez atrybutów przestrzeni nazw). Jak mogę to zrobić?

NetSide
źródło
2
Wiem, że myślisz, że może to poprawić wygląd twojego xml, ale zapewnienie przestrzeni nazw i odpowiadających im xsd jest lepszą praktyką.
2
Chcę, aby mój xml był tylko <message>, mówię o pomijaniu przestrzeni nazw xmlns: xsi i xmlns: xsd.
NetSide
5
Dla przypomnienia: generalnie jest to głupi błąd. Przestrzenie nazw istnieją z jakiegoś powodu, a usunięcie ich wszystkich zepsuje wszystko. Rzeczy takie jak deserializacja.
John Saunders
68
Zauważ, że czasami nie jest to głupie i nie pomyłka. Na przykład może zajść potrzeba wygenerowania fragmentów dokumentu i późniejszego złożenia ich razem. Osobiście musiałem wygenerować wiele podobnych i bardzo dużych dokumentów. Wszystkie miały te same duże części głęboko w drzewie. Musiałem więc wcześniej wygenerować niezmienne części i wstawić je jako tablice bajtów podczas generowania dokumentów. Aby więc wynik był bardziej czytelny i mniejszy, musiałem pominąć niektóre deklaracje przestrzeni nazw w częściach wewnętrznych, ponieważ istniały one na wyższych poziomach.
Dmitrij Taszkinow

Odpowiedzi:

235
...
XmlSerializer s = new XmlSerializer(objectToSerialize.GetType());
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("","");
s.Serialize(xmlWriter, objectToSerialize, ns);
Thomas Levesque
źródło
2
Chciałbym tylko dodać, że usunięcie domyślnej przestrzeni nazw może mieć niezamierzone konsekwencje: na przykład, jeśli użyjesz atrybutu XmlInclude do serializacji typów pochodnych, przestrzenie nazw zostaną dodane do każdego z tych elementów, czy tego chcesz, czy nie, ponieważ są konieczne do deserializacji
Thomas Levesque
3
Co więcej, nie usuwa to wszystkich przestrzeni nazw xml, jak zadawano pytanie. Usuwa tylko przestrzenie nazw xsi i xsd, jak wspomniano w pytaniu stackoverflow.com/questions/258960 , które również jest cytowane w tym pytaniu.
Cheeso
1
Nie jest również obsługiwany przez MS, jak wspomniałem w mojej własnej odpowiedzi. Nie zawsze działa, zwłaszcza gdy twój typ może być używany z innymi, które mają przestrzenie nazw.
Fourpastmidnight
@ThomasLevesque, jak usunąć domyślną przestrzeń nazw podczas korzystania z atrybutu XmlInclude?
Jeson Martajaya
4
Można skrócić dos.Serialize(writer, objectToSerialize, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }));
Xeevis,
28

To jest druga z dwóch odpowiedzi.

Jeśli chcesz po prostu usunąć wszystkie przestrzenie nazw z dokumentu podczas serializacji, możesz to zrobić, implementując własny XmlWriter.

Najłatwiejszym sposobem jest wyprowadzenie z XmlTextWriter i zastąpienie metody StartElement, która emituje przestrzenie nazw. Metoda StartElement jest wywoływana przez XmlSerializer podczas emitowania jakichkolwiek elementów, w tym katalogu głównego. Zastępując przestrzeń nazw dla każdego elementu i zastępując ją pustym ciągiem, usunęliśmy przestrzenie nazw z danych wyjściowych.

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

Załóżmy, że jest to typ:

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

Oto, jak możesz użyć takiej rzeczy podczas serializacji:

        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 jest jednak trochę uszkodzony. Zgodnie z dokumentem referencyjnym , kiedy pisze, nie sprawdza następujących elementów:

  • Nieprawidłowe znaki w nazwach atrybutów i elementów.

  • Znaki Unicode, które nie pasują do określonego kodowania. Jeśli znaki Unicode nie pasują do określonego kodowania, XmlTextWriter nie zmienia znaczenia znaków Unicode w jednostkach znakowych.

  • Zduplikowane atrybuty.

  • Znaki w publicznym identyfikatorze DOCTYPE lub identyfikatorze systemowym.

Te problemy z XmlTextWriter istniały od wersji 1.1 .NET Framework i pozostaną, ze względu na wsteczną kompatybilność. Jeśli nie masz obaw dotyczących tych problemów, zdecydowanie użyj XmlTextWriter. Ale większość ludzi chciałaby trochę większej niezawodności.

Aby to uzyskać, nadal pomijając przestrzenie nazw podczas serializacji, zamiast wyprowadzać z XmlTextWriter, zdefiniuj konkretną implementację abstrakcyjnego XmlWriter i jego 24 metod.

Oto przykład:

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

Następnie podaj klasę pochodną, ​​która zastępuje metodę StartElement, tak jak poprzednio:

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

A potem użyj tego pisarza w ten sposób:

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

Kredyt za to Oleg Tkachenko .

Ser
źródło
3
Odkryłem, że muszę również przesłonić, LookupPrefix(string ns)aby zawsze zwracać pusty ciąg, aby usunąć wszystkie deklaracje schematu.
Kevin Brock
To technicznie nie odpowiada na pytanie - używasz XmlTextWriter, a nie XmlWriter. Zauważam, ponieważ chcę używać XmlWriter dla XmlWriterSettings, których mogę z nim używać.
Abacus
@Abacus, czy przeczytałeś kod? Używa XmlWriter i XmlWriterSettings .
Cheeso
mój błąd, musiałem to przegapić.
Abacus
Świetna odpowiedź, oprócz dodanej metody z @KevinBrock, musiałem również przeciążać <! - język: lang-cs -> WriteStartAttribute (prefiks ciągu, string localName, string ns), zanim mój kod usunie wszystkie przestrzeń nazw. Warto również zauważyć, że moje prefiksy przestrzeni nazw zmieniały się z b2p1 na p2, co skłoniło mnie do sprawdzenia innych metod za pomocą prefiksów.
Mabdullah
15

Po przeczytaniu dokumentacji Microsoftu i kilku rozwiązań online, znalazłem rozwiązanie tego problemu. Działa z wbudowaną XmlSerializeri niestandardową serializacją XML za pośrednictwem IXmlSerialiazble.

Na przykład użyję tego samego MyTypeWithNamespacesprzykładu XML, który został użyty w odpowiedziach na to pytanie do tej pory.

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

To wszystko do tej klasy. Niektórzy sprzeciwiają się posiadaniu XmlSerializerNamespacesobiektu gdzieś w swoich klasach; ale jak widać, schludnie schowałem go w domyślnym konstruktorze i ujawniłem właściwość publiczną, aby zwrócić przestrzenie nazw.

Teraz, gdy przyjdzie czas na serializację klasy, użyjesz następującego kodu:

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

Gdy to zrobisz, powinieneś otrzymać następujący wynik:

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

Z powodzeniem zastosowałem tę metodę w niedawnym projekcie z głęboką hierarchią klas, które są serializowane do XML dla wywołań usług internetowych. Dokumentacja Microsoftu nie jest zbyt jasna, co zrobić z publicznie dostępnym XmlSerializerNamespacesczłonkiem po utworzeniu, a tak wielu uważa, że ​​jest bezużyteczna. Ale postępując zgodnie z ich dokumentacją i używając jej w sposób pokazany powyżej, możesz dostosować sposób, w jaki XmlSerializer generuje XML dla twoich klas bez uciekania się do nieobsługiwanego zachowania lub „toczenia własnej” serializacji przez implementację IXmlSerializable.

Mam nadzieję, że ta odpowiedź raz na zawsze położy kres temu, jak pozbyć się standardu xsii xsdprzestrzeni nazw generowanych przez XmlSerializer.

AKTUALIZACJA: Chcę się tylko upewnić, że odpowiedziałem na pytanie OP dotyczące usunięcia wszystkich przestrzeni nazw. Mój kod powyżej będzie działał w tym celu; Pokażę ci jak. Teraz, w powyższym przykładzie, naprawdę nie możesz pozbyć się wszystkich przestrzeni nazw (ponieważ są używane dwie przestrzenie nazw). Gdzieś w dokumencie XML będziesz potrzebować czegoś takiego jak xmlns="urn:Abracadabra" xmlns:w="urn:Whoohoo. Jeśli klasa w przykładzie jest częścią większego dokumentu, to gdzieś powyżej przestrzeni nazw należy zadeklarować jedną z (lub obie) Abracadbrai Whoohoo. Jeśli nie, to element w jednej lub obu przestrzeniach nazw musi być ozdobiony jakimś prefiksem (nie możesz mieć dwóch domyślnych przestrzeni nazw, prawda?). W tym przykładzie Abracadabrajest to domyślna przestrzeń nazw. Mógłbym wewnątrz mojej MyTypeWithNamespacesklasy dodać prefiks przestrzeni nazw dla Whoohooprzestrzeni nazw w następujący sposób:

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

Teraz w mojej definicji klasy wskazałem, że <Label/>element znajduje się w przestrzeni nazw "urn:Whoohoo", więc nie muszę robić nic więcej. Kiedy teraz serializuję klasę przy użyciu niezmienionego mojego powyższego kodu serializacji, jest to wynik:

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

Ponieważ <Label>znajduje się w innej przestrzeni nazw niż reszta dokumentu, w jakiś sposób musi być „ozdobiony” przestrzenią nazw. Zauważ, że nadal nie ma przestrzeni nazw xsii xsd.

o czwartej po północy
źródło
„Dokumentacja firmy Microsoft wyraźnie mówi, że nie jest obsługiwana”. Chcesz się podzielić, gdzie?
Dave Van den Eynde
Dave, jak napisałeś na mojej odpowiedzi na podobne pytanie, XmlSerializer: usuń niepotrzebne przestrzenie nazw xsi i xsd , łącze jest tutaj: XmlSerializerNamespaces Class .
czwartej północy
1
Nadal przekazujesz przestrzenie nazw do metody Serialize. Pomyślałem, że pomysł zapewnienia członka publicznego był taki, że nie musiałbyś tego robić? Nie mogę go jednak uruchomić bez przekazywania go do metody Serialize. Niestety nie mam dostępu do tego wywołania metody. Mogę tylko ustawić wystąpienie XmlSerializer do użycia.
zmiażdżyć
Okazało się, że tak naprawdę XmlWriterto jest to, co jest zawarte w tym, XmlMediaTypeFormatterco wymusza przestrzenie nazw xsi i xsd w moim wyjściu, niezależnie od tego. Dotyczy to tylko tych, którzy używają domyślnego WebApi XmlMediaTypeFormatter. Skopiowałem jego kod źródłowy i zmodyfikowałem go, aby przekazać moją właściwość Namespaces do metody Serialize, ponieważ jest to wymagane, aby zapobiec XmlWriterautomatycznemu dodawaniu dwóch wartości domyślnych. Zobacz tę odpowiedź
zmiażdż
@crush, odpowiedź, do której masz link, jest myląca - nie jest błędna, ale nie wszystkie jej twierdzenia są poprawne. Jeśli spojrzysz na pierwszy fragment kodu w mojej odpowiedzi, zobaczysz komentarz, który wyraźnie określa, jak działa XmlSerializer, gdy ujawnisz publiczny element członkowski typu XmlSerializerNamespacesozdobiony rozszerzeniem XmlNamespacesDeclarationAttribute. Zostało to pobrane bezpośrednio z MSDN i zasadniczo używa tych zadeklarowanych przestrzeni nazw zamiast domyślnych udostępnionych przez XmlSerializer.
4pastmidnight
6

To jest pierwsza z moich dwóch odpowiedzi na to pytanie.

Jeśli chcesz mieć dokładną kontrolę nad przestrzeniami nazw - na przykład jeśli chcesz pominąć niektóre z nich, ale nie inne, lub jeśli chcesz zastąpić jedną przestrzeń nazw inną, możesz to zrobić za pomocą XmlAttributeOverrides .

Załóżmy, że masz taką definicję typu:

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

I ten pseudokod serializacji:

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

Otrzymałbyś coś takiego jak ten XML:

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

Zwróć uwagę, że w elemencie głównym znajduje się domyślna przestrzeń nazw, a także odrębna przestrzeń nazw w elemencie „Label”. Te przestrzenie nazw były podyktowane atrybutami dekorującymi typ w powyższym kodzie.

Struktura serializacji Xml w .NET obejmuje możliwość jawnego zastąpienia atrybutów dekorujących rzeczywisty kod. Robisz to za pomocą klasy XmlAttributesOverrides i przyjaciół. Załóżmy, że mam ten sam typ i serializuję go w ten sposób:

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

Wynik wygląda następująco;

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

Usunąłeś przestrzenie nazw.

Logiczne pytanie brzmi: czy podczas serializacji można usunąć wszystkie przestrzenie nazw z dowolnych typów bez przechodzenia przez jawne zastąpienia? Odpowiedź brzmi TAK, a jak to zrobić, to moja następna odpowiedź.

Ser
źródło
6
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);
Tejas
źródło