Converter sequência XML em objeto

179

Estou recebendo cadeias XML em um soquete e gostaria de convertê-las em objetos C #.

As mensagens têm o formato:

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

Eu sou novo no .Net e não tenho certeza das melhores práticas para fazer isso. Eu usei o JAXB para Java antes e não tinha certeza se havia algo semelhante ou se isso seria tratado de uma maneira diferente.

Steve
fonte
3
Você tem objetos que se tornam ou deseja gerar dinamicamente os objetos?
Stephan
Para mim, este foi a melhor opção: stackoverflow.com/a/24184283/2647430
Ivan Lopez

Respostas:

277

Você precisa usar a xsd.exeferramenta que é instalada com o Windows SDK em um diretório semelhante a:

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

E em computadores de 64 bits:

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

E nos computadores com Windows 10:

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

Na primeira execução, você usa xsd.exee converte seu XML de amostra em um arquivo XSD (arquivo de esquema XML):

xsd yourfile.xml

Isso fornece a você yourfile.xsd, que em uma segunda etapa, você pode converter novamente usando xsd.exea classe C #:

xsd yourfile.xsd /c

Isso deve fornecer um arquivo yourfile.csque conterá uma classe C # que você pode usar para desserializar o arquivo XML que está recebendo - algo como:

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

Deve funcionar muito bem na maioria dos casos.

Atualização: o serializador XML aceita qualquer fluxo como entrada - um arquivo ou um fluxo de memória ficará bem:

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

ou use um StringReader:

XmlSerializer serializer = new XmlSerializer(typeof(msg));
StringReader rdr = new StringReader(inputString);
msg resultingMessage = (msg)serializer.Deserialize(rdr);
marc_s
fonte
Obrigado pela explicação detalhada. No meu caso, o XML está vindo de um soquete e é uma string. Como desserializar uma string em vez de um arquivo XML?
61110 Steve Steve
5
@ Steve: Você pode abrir um StringReader e passar o método Deserialize. StringReader deriva de TextReader.
Skurmedel 06/07/10
Você prefere sua abordagem à que Fahad mencionou usando o Linq?
Steve
2
@ Steve: sim, eu - desserializar para um objeto e ser capaz de mexer nas propriedades do objeto parece muito mais fácil do que mexer bastante com elementos XML, atributos, nós filhos etc. Linq-to-XML é ótimo se o XML é irregular e muda o tempo todo, ou não é conhecido antecipadamente.
marc_s
7
Este web site é muito mais fácil do que a ferramenta xsd IMO: xmltocsharp.azurewebsites.net
Nasch
226

Você tem duas possibilidades.

Método 1. Ferramenta XSD


Suponha que você tenha seu arquivo XML neste local C:\path\to\xml\file.xml

  1. Abra o Prompt de Comando do Desenvolvedor
    Você pode encontrá-lo em Start Menu > Programs > Microsoft Visual Studio 2012 > Visual Studio Tools Ou se você possui o Windows 8, basta começar a digitar Prompt de Comando do Desenvolvedor na tela Iniciar
  2. Altere o local para o diretório de arquivos XML digitando cd /D "C:\path\to\xml"
  3. Crie um arquivo XSD a partir do seu arquivo xml digitandoxsd file.xml
  4. Crie classes C # digitandoxsd /c file.xsd

E é isso! Você gerou classes C # do arquivo xml emC:\path\to\xml\file.cs

Método 2 - Colar especial


Necessário Visual Studio 2012 ou superior com .Net Framework> = 4.5 como destino do projeto e componente individual 'Windows Communication Foundation' instalado

  1. Copie o conteúdo do seu arquivo XML para a área de transferência
  2. Adicione à sua solução novo arquivo de classe vazio ( Shift+ Alt+ C)
  3. Abra esse arquivo e, no menu, clique em Edit > Paste special > Paste XML As Classes
    insira a descrição da imagem aqui

E é isso!

Uso


O uso é muito simples com esta classe 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());
        }
    }
}

Tudo o que você precisa fazer agora é:

    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
fonte
7
Felicidades. re: método 2, você precisa direcionar o .net 4.5, caso contrário a opção não está disponível.
timB33
12
O método 2 é ridiculamente útil! Obrigado por isso. Eu não tinha ideia de que existia.
Dominic Bindley
1
Parabéns pelo método 2, funciona como um encanto. Ótimo ao tentar simplesmente analisar um XML programaticamente sem ter que implementar classes chatas.
Alex
1
Você deve criar "Colar especial" como primeiro método - é o mais simples. A limitação ".Net Framework> = 4.5" não é importante em 2017.
Michael Freidgeim 31/12/16
2
O "Colar XML como classes" requer a carga de trabalho do WCF para o Visual Studio instalada.
Lennart #
49

Tente este método para converter Xml em um objeto. É feito exatamente para o que você está fazendo:

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

Chame-o usando este código:

YourStrongTypedEntity entity = FromXml<YourStrongTypedEntity>(YourMsgString);
RJ.
fonte
6
Ocorreu este erro xmlns = ''> não era esperado. "}, Alguma idéia?
Prashant 5/14
O problema é que você precisa ter sua turma perfeitamente formada com antecedência. Talvez uma função que gera uma classe quando recebe XML? xsd.exe é hit & miss (principalmente perder para o material complexo) ...
Yumi Koizumi
1
Oh meu Deus, eu passei horas lidando com o serializador .nets xml, e isso funcionou logo de cara.
Christopher Clark
11

Basta executar o Visual Studio 2013 como administração ... Copie o conteúdo do seu arquivo Xml. Vá para Visual Studio 2013> Editar> Colar Especial> Colar Xml como Classes C # Ele criará suas classes c # de acordo com o conteúdo do arquivo Xml.

user2667652
fonte
7

Apenas no caso de alguém achar isso ú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();
        }
    }
}

Você pode chamá-lo usando:

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

Você pode gerar a classe conforme descrito acima ou gravá-las manualmente:

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

Em seguida, você pode usar o ExtendedXmlSerializer para serializar e desserializar.

Instalação Você pode instalar o ExtendedXmlSerializer a partir do nuget ou executar o seguinte comando:

Install-Package ExtendedXmlSerializer

Serialização:

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

Desserialização

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

Este suporte ao serializador:

  • Desserialização xml do XMLSerializer padrão
  • Classe de serialização, struct, classe genérica, tipo primitivo, lista e dicionário genéricos, matriz, enum
  • Classe de serialização com interface de propriedade
  • Referência circular de serialização e ID de referência
  • Desserialização da versão antiga do xml
  • Criptografia de propriedade
  • Serializador personalizado
  • Suporte XmlElementAttribute e XmlRootAttribute
  • POCO - todas as configurações (migrações, serializador personalizado ...) estão fora da classe

O ExtendedXmlSerializer oferece suporte ao .NET 4.5 ou superior e ao .NET Core . Você pode integrá-lo ao WebApi e AspCore.

Wojtpl2
fonte
1
Excelente postagem! Atualizei o código para modernizá-lo de acordo com a documentação github.com/wojtpl2/ExtendedXmlSerializer
user1477388
2

Simplificando a grande resposta 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
fonte
1

Criar um DTO como CustomObject

Use o método abaixo para converter XML String em DTO usando 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
fonte
0

Se você tiver o xsd da mensagem xml, poderá gerar classes c # usando a ferramenta .Net xsd.exe.

Essas classes .Net podem então ser usadas para gerar o xml.

Amitabh
fonte
0

Além das outras respostas aqui, você pode usar naturalmente a classe XmlDocument , para leitura do tipo XML DOM, ou o XmlReader , leitor somente de avanço rápido, para fazê-lo "manualmente".

Skurmedel
fonte
0

Outra maneira com as ferramentas avançadas de geração de classes xsd para c #: xsd2code.com. Esta ferramenta é muito útil e poderosa. Ele tem muito mais personalização do que a ferramenta xsd.exe do Visual Studio. O Xsd2Code ++ pode ser personalizado para usar Listas ou Matrizes e suporta esquemas grandes com muitas instruções de importação.

Nota de alguns recursos,

  • Gera objetos de negócios do esquema XSD ou arquivo XML para código C # flexível ou Visual Basic.
  • Estrutura de suporte 2.0 a 4.x
  • Suporte a coleção digitada forte (List, ObservableCollection, MyCustomCollection).
  • Suporte propriedades automáticas.
  • Gere métodos de leitura e gravação XML (serialização / desserialização).
  • Suporte à ligação de dados (WPF, Xamarin).
  • WCF (atributo DataMember).
  • Suporte para codificação XML (UTF-8/32, ASCII, Unicode, personalizado).
  • Camel case / Pascal Case support.
  • suporte à restrição ([StringLengthAttribute = true / false], [RegularExpressionAttribute = true / false], [RangeAttribute = true / false]).
  • Suporte para arquivo XSD grande e complexo.
  • Suporte do DotNet Core e padrão
Haas Franck
fonte
0

Sei que essa pergunta é antiga, mas me deparei com ela e tenho uma resposta diferente de, bem, todo mundo :-)

A maneira usual (como os comentaristas acima mencionam) é gerar uma classe e desserializar seu xml.

Mas ( aviso: autopromoção desavergonhada aqui ) acabei de publicar um pacote de pepitas aqui , com o qual você não precisa. Você apenas vai:

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

É isso literalmente, nada mais é necessário. E, o mais importante, se o seu xml for alterado, o objeto também será alterado automaticamente.

Se você preferir baixar a dll diretamente, a página do github está aqui .

Jim Andrakakis
fonte
-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
fonte
5
É assim que serializar, não como desserializar.
alexc95
1
Você acabou de escrever o código aqui. Sem explicação, é sem sentido para muitos.
M. Haché 21/07
Código não descarta fluxos
bigfoot