Jak serializować obiekt do XML bez pobierania xmlns = „…”?

109

Czy istnieje sposób na serializowanie obiektu w .NET bez automatycznej serializacji przestrzeni nazw XML? Wygląda na to, że domyślnie .NET uważa, że ​​obszary nazw XSI i XSD powinny być uwzględnione, ale nie chcę ich tam.

Wes P
źródło

Odpowiedzi:

142

Ahh ... nieważne. Zawsze szukanie po zadaniu pytania daje odpowiedź. Mój obiekt, który jest serializowany, jest obji został już zdefiniowany. Dodanie XMLSerializerNamespace z pojedynczą pustą przestrzenią nazw do kolekcji załatwia sprawę.

W VB w ten sposób:

Dim xs As New XmlSerializer(GetType(cEmploymentDetail))
Dim ns As New XmlSerializerNamespaces()
ns.Add("", "")

Dim settings As New XmlWriterSettings()
settings.OmitXmlDeclaration = True

Using ms As New MemoryStream(), _
    sw As XmlWriter = XmlWriter.Create(ms, settings), _
    sr As New StreamReader(ms)
xs.Serialize(sw, obj, ns)
ms.Position = 0
Console.WriteLine(sr.ReadToEnd())
End Using

w C # w ten sposób:

//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);
Wes P
źródło
12
Próbowałem tego w VB, atrybuty xsi i xsd zniknęły, ale pojawiły się atrybuty takie jak xmlns: q12 =, d3p1: type i xmlns: d3p1.
MiddleKay,
16
Wypróbowałem wersję C # i usunąłem xsi i xsd, ale dodałem prefiks q1: do wszystkich nazw tagów XML, których nie chciałem. Wygląda na to, że przykład C # jest niekompletny, odwołując się do myXmlTextWriter, który, jak zakładam, musi zostać zainicjowany w taki sam sposób, jak przykład VB.
czworościan
1
@redtetrahedron Czy znalazłeś sposób na pozbycie się tego q1gówna?
zmiażdżyć
Zapoznaj się z odpowiedzią stackoverflow.com/questions/31946240/… , jeśli q1 został dodany jako pusta przestrzeń nazw
aniruddha
20

Jeśli chcesz pozbyć się tego dodatkowego xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"i xmlns:xsd="http://www.w3.org/2001/XMLSchema"zachować własną przestrzeń nazw xmlns="http://schemas.YourCompany.com/YourSchema/", używasz tego samego kodu co powyżej, z wyjątkiem tej prostej zmiany:

//  Add lib namespace with empty prefix  
ns.Add("", "http://schemas.YourCompany.com/YourSchema/");   
Ali B.
źródło
13

Jeśli chcesz usunąć przestrzeń nazw, możesz również usunąć wersję, aby zaoszczędzić na wyszukiwaniu, dodałem tę funkcjonalność, więc poniższy kod będzie spełniał obie te funkcje.

Zapakowałem to również w ogólną metodę, ponieważ tworzę bardzo duże pliki xml, które są zbyt duże, aby można je było serializować w pamięci, więc zepsułem mój plik wyjściowy i serializuję go w mniejszych „kawałkach”:

    public static string XmlSerialize<T>(T entity) where T : class
    {
        // removes version
        XmlWriterSettings settings = new XmlWriterSettings();
        settings.OmitXmlDeclaration = true;

        XmlSerializer xsSubmit = new XmlSerializer(typeof(T));
        using (StringWriter sw = new StringWriter())
        using (XmlWriter writer = XmlWriter.Create(sw, settings))
        {
            // removes namespace
            var xmlns = new XmlSerializerNamespaces();
            xmlns.Add(string.Empty, string.Empty);

            xsSubmit.Serialize(writer, entity, xmlns);
            return sw.ToString(); // Your XML
        }
    }
D34th
źródło
Uwaga, StringWriterdomyślnie kodowanie UTF-16 może prowadzić do problemów z deserializacją. using (var reader = XmlReader.Create(stream)){ reader.Read(); }Powoduje to zgłoszenie wyjątku, ponieważ deklaracja stwierdza, że ​​jest to UTF-16, podczas gdy zawartość została faktycznie zapisana jako UTF-8. System.Xml.XmlException: 'There is no Unicode byte order mark. Cannot switch to Unicode.'
Tyler StandishMan
Aby obejść ten problem i nadal używać XmlReader, możesz użyć opcji var streamReader = new StreamReader(stream, System.Text.Encoding.UTF8, true);Prawda użyje zestawienia komponentów, jeśli zostanie znaleziony, w przeciwnym razie wartość domyślna, którą podasz.
Tyler StandishMan
9

Proponuję tę klasę pomocniczą:

public static class Xml
{
    #region Fields

    private static readonly XmlWriterSettings WriterSettings = new XmlWriterSettings {OmitXmlDeclaration = true, Indent = true};
    private static readonly XmlSerializerNamespaces Namespaces = new XmlSerializerNamespaces(new[] {new XmlQualifiedName("", "")});

    #endregion

    #region Methods

    public static string Serialize(object obj)
    {
        if (obj == null)
        {
            return null;
        }

        return DoSerialize(obj);
    }

    private static string DoSerialize(object obj)
    {
        using (var ms = new MemoryStream())
        using (var writer = XmlWriter.Create(ms, WriterSettings))
        {
            var serializer = new XmlSerializer(obj.GetType());
            serializer.Serialize(writer, obj, Namespaces);
            return Encoding.UTF8.GetString(ms.ToArray());
        }
    }

    public static T Deserialize<T>(string data)
        where T : class
    {
        if (string.IsNullOrEmpty(data))
        {
            return null;
        }

        return DoDeserialize<T>(data);
    }

    private static T DoDeserialize<T>(string data) where T : class
    {
        using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(data)))
        {
            var serializer = new XmlSerializer(typeof (T));
            return (T) serializer.Deserialize(ms);
        }
    }

    #endregion
}

:)

Maziar Taheri
źródło
świetna odpowiedź :) dodałem również ten strumień linii. Pozycja = 0; i
zwróciłem
Samo dodanie argumentu przestrzeni nazw do wywołania serializatora pomogło mi usunąć domyślne przestrzenie nazw. Moim zdaniem pisanie new XmlSerializerNamespaces(new[] {XmlQualifiedName.Empty})zamiast pisania new XmlSerializerNamespaces(new[] {new XmlQualifiedName("", "")})jest celowo jaśniejszym sposobem kodowania.
Suncat2000
5

Jeśli nie możesz pozbyć się dodatkowych atrybutów xmlns dla każdego elementu, podczas serializacji do xml z wygenerowanych klas (np .: gdy został użyty xsd.exe ), to masz coś takiego:

<manyElementWith xmlns="urn:names:specification:schema:xsd:one" />

następnie podzielę się z tobą tym, co zadziałało dla mnie (mieszanka poprzednich odpowiedzi i tego, co tutaj znalazłem )

jawnie ustaw wszystkie swoje różne xmlns w następujący sposób:

Dim xmlns = New XmlSerializerNamespaces()
xmlns.Add("one", "urn:names:specification:schema:xsd:one")
xmlns.Add("two",  "urn:names:specification:schema:xsd:two")
xmlns.Add("three",  "urn:names:specification:schema:xsd:three")

następnie przekaż go do serializacji

serializer.Serialize(writer, object, xmlns);

będziesz mieć trzy przestrzenie nazw zadeklarowane w elemencie głównym i nie będzie już konieczne ich generowanie w innych elementach, które zostaną odpowiednio poprzedzone

<root xmlns:one="urn:names:specification:schema:xsd:one" ... />
   <one:Element />
   <two:ElementFromAnotherNameSpace /> ...
vinjenzo
źródło
0
        XmlWriterSettings settings = new XmlWriterSettings
        {
            OmitXmlDeclaration = true
        };

        XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
        ns.Add("", "");

        StringBuilder sb = new StringBuilder();

        XmlSerializer xs = new XmlSerializer(typeof(BankingDetails));

        using (XmlWriter xw = XmlWriter.Create(sb, settings))
        {
            xs.Serialize(xw, model, ns);
            xw.Flush();
            return sb.ToString();
        }
Taurus999able
źródło