¿Cómo serializar un objeto a XML sin obtener xmlns = “…”?

109

¿Hay alguna manera de serializar un objeto en .NET sin que los espacios de nombres XML también se serialicen automáticamente? Parece que, de forma predeterminada, .NET cree que los espacios de nombres XSI y XSD deberían incluirse, pero no los quiero allí.

Wes P
fuente

Respuestas:

142

Ahh ... no importa. Siempre es la búsqueda después de que se plantea la pregunta lo que da la respuesta. Mi objeto que se está serializando está objy ya se ha definido. Agregar un XMLSerializerNamespace con un solo espacio de nombres vacío a la colección hace el truco.

En VB así:

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

en C # así:

//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
fuente
12
Intenté esto en VB, los atributos xsi y xsd desaparecieron, pero aparecieron atributos como xmlns: q12 =, d3p1: type y xmlns: d3p1.
MiddleKay
16
Probé la versión C # y eliminó xsi y xsd, pero agregué un prefijo de q1: a todos los nombres de etiquetas XML, que no quería. Parece que el ejemplo de C # está incompleto, haciendo referencia a myXmlTextWriter, que supongo que debe inicializarse de la misma manera que el ejemplo de VB.
redtetraedro
1
@redtetrahedron ¿Encontraste una manera de deshacerte de la q1basura?
aplasta el
Consulte la respuesta stackoverflow.com/questions/31946240/… , si q1 se agregó como espacio de nombres en blanco
aniruddha
20

Si desea deshacerse del extra xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"y xmlns:xsd="http://www.w3.org/2001/XMLSchema", pero aún conserva su propio espacio de nombres xmlns="http://schemas.YourCompany.com/YourSchema/", use el mismo código que el anterior, excepto por este simple cambio:

//  Add lib namespace with empty prefix  
ns.Add("", "http://schemas.YourCompany.com/YourSchema/");   
Ali B
fuente
13

Si desea eliminar el espacio de nombres, es posible que también desee eliminar la versión, para evitar la búsqueda, agregué esa funcionalidad, por lo que el código a continuación hará ambas cosas.

También lo he incluido en un método genérico, ya que estoy creando archivos xml muy grandes que son demasiado grandes para serializar en la memoria, así que he dividido mi archivo de salida y lo serializo en "trozos" más pequeños:

    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
fuente
Tenga cuidado, el valor StringWriterpredeterminado es la codificación UTF-16, lo que puede provocar problemas de deserialización en sentido descendente. using (var reader = XmlReader.Create(stream)){ reader.Read(); }Esto genera una excepción porque la declaración indica que es UTF-16, mientras que el contenido en realidad se escribió como UTF-8. System.Xml.XmlException: 'There is no Unicode byte order mark. Cannot switch to Unicode.'
Tyler StandishMan
Para evitar esto y seguir utilizándolo XmlReader, puede utilizar var streamReader = new StreamReader(stream, System.Text.Encoding.UTF8, true);El verdadero utilizará la lista de materiales si se encuentra, de lo contrario, el predeterminado que proporcione.
Tyler StandishMan
9

Sugiero esta clase de ayuda:

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
fuente
gran respuesta :) También agregué esta línea stream.Position = 0; y devolví el flujo completo en mi solución ... funcionó como se esperaba - se eliminaron todas las etiquetas de desaceleración
ymz
Agregar el argumento de los espacios de nombres a la llamada del serializador solo funcionó para eliminar los espacios de nombres predeterminados. Escribir en new XmlSerializerNamespaces(new[] {XmlQualifiedName.Empty})lugar de new XmlSerializerNamespaces(new[] {new XmlQualifiedName("", "")})es una forma intencionalmente más clara de codificarlo, en mi opinión.
Suncat2000
5

Si no puede deshacerse de los atributos xmlns adicionales para cada elemento, al serializar a xml desde clases generadas (por ejemplo: cuando se usó xsd.exe ), entonces tiene algo como:

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

luego compartiría con ustedes lo que funcionó para mí (una combinación de respuestas anteriores y lo que encontré aquí )

establezca explícitamente todos sus xmlns diferentes de la siguiente manera:

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

luego pasarlo a serializar

serializer.Serialize(writer, object, xmlns);

tendrá los tres espacios de nombres declarados en el elemento raíz y no será necesario generar más en los otros elementos que se prefijarán en consecuencia

<root xmlns:one="urn:names:specification:schema:xsd:one" ... />
   <one:Element />
   <two:ElementFromAnotherNameSpace /> ...
vinjenzo
fuente
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();
        }
Tauro999able
fuente