Convertir cadena XML a objeto

179

Estoy recibiendo cadenas XML sobre un socket, y me gustaría convertirlas en objetos C #.

Los mensajes son de la forma:

<msg>
   <id>1</id>
   <action>stop</action>
</msg>

Soy nuevo en .Net, y no estoy seguro de la mejor práctica para realizar esto. He usado JAXB para Java anteriormente, y no estaba seguro de si hay algo similar, o si esto se manejaría de una manera diferente.

Steve
fuente
3
¿Tiene objetos en los que se convierten o desea generar dinámicamente los objetos?
Stephan
Para mí esta fue la mejor opción: stackoverflow.com/a/24184283/2647430
Ivan Lopez

Respuestas:

277

Debe usar la xsd.exeherramienta que se instala con el SDK de Windows en un directorio similar a:

C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin

Y en computadoras de 64 bits:

C:\Program Files (x86)\Microsoft SDKs\Windows\v6.0A\bin

Y en computadoras con Windows 10:

C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin

En la primera ejecución, usa xsd.exey convierte su XML de muestra en un archivo XSD (archivo de esquema XML):

xsd yourfile.xml

Esto le da yourfile.xsd, que en un segundo paso, puede convertir nuevamente usando xsd.exeuna clase de C #:

xsd yourfile.xsd /c

Esto debería darle un archivo yourfile.csque contendrá una clase C # que puede usar para deserializar el archivo XML que está obteniendo, algo como:

XmlSerializer serializer = new XmlSerializer(typeof(msg));
msg resultingMessage = (msg)serializer.Deserialize(new XmlTextReader("yourfile.xml"));

Debería funcionar bastante bien para la mayoría de los casos.

Actualización: el serializador XML tomará cualquier flujo como su entrada, ya sea un archivo o un flujo de memoria estará bien:

XmlSerializer serializer = new XmlSerializer(typeof(msg));
MemoryStream memStream = new MemoryStream(Encoding.UTF8.GetBytes(inputString));
msg resultingMessage = (msg)serializer.Deserialize(memStream);

o use un StringReader:

XmlSerializer serializer = new XmlSerializer(typeof(msg));
StringReader rdr = new StringReader(inputString);
msg resultingMessage = (msg)serializer.Deserialize(rdr);
marc_s
fuente
Gracias por la explicación detallada. En mi caso, el XML está llegando a un socket y es una cadena. ¿Cómo des-serializaría una cadena en lugar de un archivo XML?
Steve
55
@Steve: puede abrir un StringReader y pasar el método Deserialize. StringReader deriva de TextReader.
Skurmedel
¿Preferiría su enfoque al que Fahad mencionó usando Linq?
Steve
2
@Steve: sí, lo haría: deserializar en un objeto y poder hurgar en las propiedades del objeto parece mucho más fácil que hacer muchos movimientos con elementos XML, atributos, nodos secundarios, etc. Linq-to-XML es genial si el XML es irregular y cambia todo el tiempo, o no se conoce con anticipación.
marc_s
77
Este sitio web es mucho más fácil que la herramienta xsd IMO: xmltocsharp.azurewebsites.net
nasch
226

Tienes dos posibilidades.

Método 1. Herramienta XSD


Suponga que tiene su archivo XML en esta ubicación C:\path\to\xml\file.xml

  1. Abra el símbolo del sistema del desarrollador
    Puede encontrarlo en Start Menu > Programs > Microsoft Visual Studio 2012 > Visual Studio Tools O si tiene Windows 8 puede comenzar a escribir el símbolo del sistema del desarrollador en la pantalla de inicio
  2. Cambie la ubicación a su directorio de archivos XML escribiendo cd /D "C:\path\to\xml"
  3. Cree un archivo XSD a partir de su archivo xml escribiendoxsd file.xml
  4. Cree clases de C # escribiendoxsd /c file.xsd

¡Y eso es! Ha generado clases de C # desde un archivo xml enC:\path\to\xml\file.cs

Método 2 - Pasta especial


Requiere Visual Studio 2012+ con .Net Framework> = 4.5 como objetivo del proyecto y componente individual 'Windows Communication Foundation' instalado

  1. Copie el contenido de su archivo XML al portapapeles
  2. Agregue a su solución un nuevo archivo de clase vacío ( Shift+ Alt+ C)
  3. Abra ese archivo y en el menú haga clic Edit > Paste special > Paste XML As Classes
    ingrese la descripción de la imagen aquí

¡Y eso es!

Uso


El uso es muy simple con esta clase auxiliar:

using System;
using System.IO;
using System.Web.Script.Serialization; // Add reference: System.Web.Extensions
using System.Xml;
using System.Xml.Serialization;

namespace Helpers
{
    internal static class ParseHelpers
    {
        private static JavaScriptSerializer json;
        private static JavaScriptSerializer JSON { get { return json ?? (json = new JavaScriptSerializer()); } }

        public static Stream ToStream(this string @this)
        {
            var stream = new MemoryStream();
            var writer = new StreamWriter(stream);
            writer.Write(@this);
            writer.Flush();
            stream.Position = 0;
            return stream;
        }


        public static T ParseXML<T>(this string @this) where T : class
        {
            var reader = XmlReader.Create(@this.Trim().ToStream(), new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Document });
            return new XmlSerializer(typeof(T)).Deserialize(reader) as T;
        }

        public static T ParseJSON<T>(this string @this) where T : class
        {
            return JSON.Deserialize<T>(@this.Trim());
        }
    }
}

Todo lo que tienes que hacer ahora es:

    public class JSONRoot
    {
        public catalog catalog { get; set; }
    }
    // ...

    string xml = File.ReadAllText(@"D:\file.xml");
    var catalog1 = xml.ParseXML<catalog>();

    string json = File.ReadAllText(@"D:\file.json");
    var catalog2 = json.ParseJSON<JSONRoot>();
Damian Drygiel
fuente
77
salud. re: método 2, debe apuntar a .net 4.5 de lo contrario, la opción no está disponible.
timB33
12
¡El método 2 es ridículamente útil! Gracias por eso. No tenía idea de que existía.
Dominic Bindley
1
Felicitaciones por el método 2, funciona como un encanto. Excelente cuando se trata simplemente de analizar un XML mediante programación sin tener que implementar clases aburridas.
Alex
1
Debe hacer "Pegado especial" como primer método, es el más simple. La limitación ".Net Framework> = 4.5" no es importante en 2017.
Michael Freidgeim
2
El "Pegar XML como clases" requiere la carga de trabajo WCF para Visual Studio instalado.
Lennart el
49

Pruebe este método para convertir Xml a un objeto. Está hecho exactamente para lo que está haciendo:

protected T FromXml<T>(String xml)
{
    T returnedXmlClass = default(T);

    try
    {
        using (TextReader reader = new StringReader(xml))
        {
            try
            {
                returnedXmlClass = 
                    (T)new XmlSerializer(typeof(T)).Deserialize(reader);
            }
            catch (InvalidOperationException)
            {
                // String passed is not XML, simply return defaultXmlClass
            }
        }
    }
    catch (Exception ex)
    {
    }

    return returnedXmlClass ;        
}

Llámalo usando este código:

YourStrongTypedEntity entity = FromXml<YourStrongTypedEntity>(YourMsgString);
RJ.
fuente
66
Tengo este error xmlns = ''> no se esperaba. "}, ¿
Alguna
El problema es que necesitas tener tu clase perfectamente formada de antemano. ¿Quizás una función que genera una clase cuando se le da XML? xsd.exe es hit & miss (en su mayoría miss para cosas complejas) ...
Yumi Koizumi
1
Dios mío, pasé horas tratando con el serializador .nets xml, y esto funcionó desde el principio.
Christopher Clark
11

Simplemente ejecute su Visual Studio 2013 como Administración ... Copie el contenido de su archivo Xml. Vaya a Visual Studio 2013> Editar> Pegado especial> Pegar Xml como clases C # Creará sus clases C # de acuerdo con el contenido de su archivo Xml.

usuario2667652
fuente
7

En caso de que alguien pueda encontrar esto útil:

public static class XmlConvert
{
    public static string SerializeObject<T>(T dataObject)
    {
        if (dataObject == null)
        {
            return string.Empty;
        }
        try
        {
            using (StringWriter stringWriter = new System.IO.StringWriter())
            {
                var serializer = new XmlSerializer(typeof(T));
                serializer.Serialize(stringWriter, dataObject);
                return stringWriter.ToString();
            }
        }
        catch (Exception ex)
        {
            return string.Empty;
        }
    }

    public static T DeserializeObject<T>(string xml)
         where T : new()
    {
        if (string.IsNullOrEmpty(xml))
        {
            return new T();
        }
        try
        {
            using (var stringReader = new StringReader(xml))
            {
                var serializer = new XmlSerializer(typeof(T));
                return (T)serializer.Deserialize(stringReader);
            }
        }
        catch (Exception ex)
        {
            return new T();
        }
    }
}

Puedes llamarlo usando:

MyCustomObject myObject = new MyCustomObject();
string xmlString = XmlConvert.SerializeObject(myObject)
myObject = XmlConvert.DeserializeObject<MyCustomObject>(xmlString);
Razzer
fuente
5

Puede generar la clase como se describe anteriormente, o escribirlos manualmente:

[XmlRoot("msg")]
public class Message
{
    [XmlElement("id")]
    public string Id { get; set; }
    [XmlElement("action")]
    public string Action { get; set; }
}

Luego puede usar ExtendedXmlSerializer para serializar y deserializar.

Instalación Puede instalar ExtendedXmlSerializer desde nuget o ejecutar el siguiente comando:

Install-Package ExtendedXmlSerializer

Publicación por entregas:

var serializer = new ConfigurationContainer().Create();
var obj = new Message();
var xml = serializer.Serialize(obj);

Deserialización

var obj2 = serializer.Deserialize<Message>(xml);

Este soporte de serializador:

  • Deserialización xml de XMLSerializer estándar
  • Clase de serialización, estructura, clase genérica, tipo primitivo, lista genérica y diccionario, matriz, enumeración
  • Clase de serialización con interfaz de propiedad
  • Referencia circular de serialización e ID de referencia
  • Deserialización de la versión anterior de xml
  • Cifrado de propiedad
  • Serializador personalizado
  • Soporte XmlElementAttribute y XmlRootAttribute
  • POCO: todas las configuraciones (migraciones, serializador personalizado ...) están fuera de la clase

ExtendedXmlSerializer admite .NET 4.5 o superior y .NET Core . Puede integrarlo con WebApi y AspCore.

Wojtpl2
fuente
1
Excelente post! Actualicé
user1477388
2

Simplificando la gran respuesta de Damian,

public static T ParseXml<T>(this string value) where T : class
{
    var xmlSerializer = new XmlSerializer(typeof(T));
    using (var textReader = new StringReader(value))
    {
        return (T) xmlSerializer.Deserialize(textReader);
    }
}
Sam Jazz
fuente
1

Crear un DTO como CustomObject

Utilice el siguiente método para convertir cadenas XML a DTO utilizando JAXB

private static CustomObject getCustomObject(final String ruleStr) {
    CustomObject customObject = null;
    try {
        JAXBContext jaxbContext = JAXBContext.newInstance(CustomObject.class);
        final StringReader reader = new StringReader(ruleStr);
        Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
        customObject = (CustomObject) jaxbUnmarshaller.unmarshal(reader);
    } catch (JAXBException e) {
        LOGGER.info("getCustomObject parse error: ", e);
    }
    return customObject;
}
Mohit Singh
fuente
0

Si tiene el xsd del mensaje xml, puede generar clases de C # con la herramienta .Net xsd.exe.

Estas clases .Net se pueden usar para generar el xml.

Amitabh
fuente
0

Además de las otras respuestas aquí, puede usar naturalmente la clase XmlDocument , para lectura similar a XML DOM, o el XmlReader , lector de avance rápido, para hacerlo "a mano".

Skurmedel
fuente
0

Otra forma con herramientas avanzadas de generación de clases de xsd a c #: xsd2code.com. Esta herramienta es muy útil y poderosa. Tiene mucha más personalización que la herramienta xsd.exe de Visual Studio. Xsd2Code ++ se puede personalizar para usar listas o matrices y admite esquemas grandes con muchas declaraciones de importación.

Nota de algunas características,

  • Genera objetos comerciales a partir de un esquema XSD o un archivo XML en código C # o Visual Basic flexible.
  • Marco de soporte 2.0 a 4.x
  • Admite una colección con tipo fuerte (List, ObservableCollection, MyCustomCollection).
  • Soporta propiedades automáticas.
  • Generar métodos de lectura y escritura XML (serialización / deserialización).
  • Soporte de enlace de datos (WPF, Xamarin).
  • WCF (atributo DataMember).
  • Soporte de codificación XML (UTF-8/32, ASCII, Unicode, Personalizado).
  • Funda Camel / Soporte Pascal Case.
  • soporte de restricción ([StringLengthAttribute = verdadero / falso], [RegularExpressionAttribute = verdadero / falso], [RangeAttribute = verdadero / falso]).
  • Admite archivos XSD grandes y complejos.
  • Soporte de DotNet Core y estándar
Haas Franck
fuente
0

Sé que esta pregunta es antigua, pero me topé con ella y tengo una respuesta diferente que, bueno, todos los demás :-)

La forma habitual (como mencionan los comentaristas anteriores) es generar una clase y deserializar su xml.

Pero ( advertencia: autopromoción descarada aquí ) acabo de publicar un paquete nuget, aquí , con el que no tienes que hacerlo. Solo tienes que ir:

string xml = System.IO.File.ReadAllText(@"C:\test\books.xml");
var book = Dandraka.XmlUtilities.XmlSlurper.ParseText(xml);

Eso es literalmente, no se necesita nada más. Y, lo más importante, si su xml cambia, su objeto también cambia automáticamente.

Si prefiere descargar el dll directamente, la página de github está aquí .

Jim Andrakakis
fuente
-7
public string Serialize<T>(T settings)
{
    XmlSerializer serializer = new XmlSerializer(typeof(T));
    StringWriter outStream = new StringWriter();
    serializer.Serialize(outStream, settings);
    return outStream.ToString();
}
Mandoleen
fuente
55
Así es como serializar, no cómo deserializar.
alexc95
1
Acabas de escribir código aquí. Sin explicación, no tiene sentido para muchos.
M. Haché
El código no elimina las transmisiones
bigfoot