Convertir une chaîne XML en objet


184

Je reçois des chaînes XML sur une socket et j'aimerais les convertir en objets C #.

Les messages sont de la forme:

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

Je suis nouveau sur .Net et je ne suis pas sûr de la meilleure pratique pour effectuer cela. J'ai déjà utilisé JAXB pour Java, et je ne savais pas s'il y avait quelque chose de similaire ou si cela serait géré d'une manière différente.


3
Avez-vous des objets qu'ils deviennent ou souhaitez-vous générer dynamiquement les objets?
Stephan


Pour moi, c'était la meilleure option: stackoverflow.com/a/24184283/2647430
Ivan Lopez

Réponses:


281

Vous devez utiliser l' xsd.exeoutil qui est installé avec le SDK Windows dans un répertoire similaire à:

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

Et sur les ordinateurs 64 bits:

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

Et sur les ordinateurs Windows 10:

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

Lors de la première exécution, vous utilisez xsd.exeet vous convertissez votre exemple de XML en un fichier XSD (fichier de schéma XML):

xsd yourfile.xml

Cela vous donne yourfile.xsd, que dans un deuxième temps, vous pouvez convertir à nouveau en utilisant xsd.exeune classe C #:

xsd yourfile.xsd /c

Cela devrait vous donner un fichier yourfile.csqui contiendra une classe C # que vous pouvez utiliser pour désérialiser le fichier XML que vous obtenez - quelque chose comme:

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

Cela devrait plutôt bien fonctionner dans la plupart des cas.

Mise à jour: le sérialiseur XML prendra n'importe quel flux comme entrée - un fichier ou un flux mémoire conviendra:

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

ou utilisez un StringReader:

XmlSerializer serializer = new XmlSerializer(typeof(msg));
StringReader rdr = new StringReader(inputString);
msg resultingMessage = (msg)serializer.Deserialize(rdr);

Merci pour l'explication détaillée. Dans mon cas, le XML arrive sur une socket et est une chaîne. Comment désérialiser une chaîne au lieu d'un fichier XML?
Steve

5
@Steve: Vous pouvez ouvrir un StringReader et transmettre la méthode Deserialize. StringReader dérive de TextReader.
Skurmedel

Préférez-vous votre approche à celle mentionnée par Fahad en utilisant Linq?
Steve

2
@Steve: oui, je le ferais - désérialiser dans un objet et être capable de pousser les propriétés de l'objet semble beaucoup plus facile que de faire beaucoup de tortillements avec des éléments XML, des attributs, des nœuds enfants, etc. Linq-to-XML est génial si le XML est irrégulier et change tout le temps, ou n'est pas connu à l'avance.
marc_s

7
Ce site Web est beaucoup plus facile que l'outil xsd IMO: xmltocsharp.azurewebsites.net
nasch

233

Vous avez deux possibilités.

Méthode 1. Outil XSD


Supposons que vous ayez votre fichier XML à cet emplacement C:\path\to\xml\file.xml

  1. Ouvrez l' invite de commande du développeur
    Vous pouvez le trouver dans Start Menu > Programs > Microsoft Visual Studio 2012 > Visual Studio Tools Ou si vous avez Windows 8, vous pouvez simplement commencer à taper l' invite de commande du développeur dans l' écran de démarrage
  2. Changez l'emplacement de votre répertoire de fichiers XML en tapant cd /D "C:\path\to\xml"
  3. Créez un fichier XSD à partir de votre fichier xml en tapantxsd file.xml
  4. Créer des classes C # en tapantxsd /c file.xsd

Et c'est tout! Vous avez généré des classes C # à partir d'un fichier xml dansC:\path\to\xml\file.cs

Méthode 2 - Collage spécial


Visual Studio 2012+ requis avec .Net Framework> = 4.5 en tant que cible du projet et composant individuel «Windows Communication Foundation» installé

  1. Copiez le contenu de votre fichier XML dans le presse-papiers
  2. Ajoutez à votre solution un nouveau fichier de classe vide ( Shift+ Alt+ C)
  3. Ouvrez ce fichier et dans le menu, cliquez sur Edit > Paste special > Paste XML As Classes
    entrez la description de l'image ici

Et c'est tout!

Usage


L'utilisation est très simple avec cette classe d'assistance:

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

Tout ce que vous avez à faire maintenant, c'est:

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

7
à votre santé. re: méthode 2, vous devez cibler .net 4.5 sinon l'option n'est pas disponible.
timB33

12
La méthode 2 est ridiculement utile! Merci pour ça. Je n'avais aucune idée de ce qui existait.
Dominic Bindley

1
Félicitations pour la méthode 2, fonctionne comme un charme. Excellent lorsque vous essayez d'analyser simplement un XML par programme sans avoir à implémenter des classes ennuyeuses.
Alex

1
Vous devriez faire "Collage spécial" comme première méthode - c'est la plus simple. La limitation ".Net Framework> = 4.5" n'est pas importante en 2017.
Michael Freidgeim

2
Le «Coller XML en tant que classes» nécessite l'installation de la charge de travail WCF pour Visual Studio.
Lennart

51

Essayez cette méthode pour convertir du XML en objet. Il est fait exactement pour ce que vous faites:

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

Appelez-le en utilisant ce code:

YourStrongTypedEntity entity = FromXml<YourStrongTypedEntity>(YourMsgString);

6
Vous avez cette erreur xmlns = ''> n'était pas prévu. "}, Une idée?
Prashant

Le problème est que vous devez avoir votre classe parfaitement formée à l'avance. Peut-être une fonction qui génère une classe lorsqu'elle est donnée XML? xsd.exe is hit & miss (surtout miss for complex stuff) ...
Yumi Koizumi

1
Oh mon dieu, j'ai passé des heures à gérer le sérialiseur .nets xml, et cela a fonctionné dès le départ.
christopher clark

12

Exécutez simplement votre Visual Studio 2013 en tant qu'administration ... Copiez le contenu de votre fichier Xml. Allez dans Visual Studio 2013> Edition> Collage spécial> Coller Xml en tant que classes C #. Il créera vos classes c # en fonction du contenu de votre fichier Xml.


8

Juste au cas où quelqu'un trouverait cela utile:

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

Vous pouvez l'appeler en utilisant:

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

6

Vous pouvez générer des classes comme décrit ci-dessus, ou les écrire manuellement:

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

Ensuite, vous pouvez utiliser ExtendedXmlSerializer pour sérialiser et désérialiser.

Instalation Vous pouvez installer ExtendedXmlSerializer à partir NuGet ou exécutez la commande suivante:

Install-Package ExtendedXmlSerializer

Sérialisation:

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

Désérialisation

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

Ce sérialiseur prend en charge:

  • Désérialisation XML à partir de XMLSerializer standard
  • Classe de sérialisation, struct, classe générique, type primitif, liste et dictionnaire génériques, tableau, enum
  • Classe de sérialisation avec interface de propriété
  • Référence circulaire de sérialisation et ID de référence
  • Désérialisation de l'ancienne version de xml
  • Cryptage de propriété
  • Sérialiseur personnalisé
  • Prise en charge de XmlElementAttribute et XmlRootAttribute
  • POCO - toutes les configurations (migrations, sérialiseur personnalisé ...) sont en dehors de la classe

ExtendedXmlSerializer prend en charge .NET 4.5 ou version ultérieure et .NET Core . Vous pouvez l'intégrer avec WebApi et AspCore.


1
Excellent post! J'ai mis à jour le code pour le moderniser selon la documentation github.com/wojtpl2/ExtendedXmlSerializer
user1477388


2

Simplifier la grande réponse 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);
    }
}

1

Je sais que cette question est ancienne, mais je suis tombé dessus et j'ai une réponse différente de celle de tous les autres :-)

La manière habituelle (comme le mentionnent les commentateurs ci-dessus) est de générer une classe et de désérialiser votre xml.

Mais ( attention: auto-promotion éhontée ici ) Je viens de publier un paquet nuget, ici , avec lequel vous n'avez pas à le faire. Allez simplement:

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

C'est littéralement ça, rien d'autre n'est nécessaire. Et, plus important encore, si votre XML change, votre objet change également automatiquement.

Si vous préférez télécharger la dll directement, la page github est ici .


1

Créer un DTO en tant que CustomObject

Utilisez la méthode ci-dessous pour convertir la chaîne XML en DTO à l'aide de 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;
}

0

Si vous disposez du xsd du message xml, vous pouvez générer des classes c # à l'aide de l'outil .Net xsd.exe.

Ces classes .Net peuvent ensuite être utilisées pour générer le xml.


0

En plus des autres réponses ici, vous pouvez naturellement utiliser la classe XmlDocument , pour une lecture de type XML DOM, ou le XmlReader , lecteur à avance rapide uniquement, pour le faire "à la main".


0

Une autre façon avec un outil avancé de génération de classes xsd vers c #: xsd2code.com. Cet outil est très pratique et puissant. Il a beaucoup plus de personnalisation que l'outil xsd.exe de Visual Studio. Xsd2Code ++ peut être personnalisé pour utiliser des listes ou des tableaux et prend en charge de grands schémas avec de nombreuses instructions d'importation.

Note de quelques fonctionnalités,

  • Génère des objets métier à partir d'un schéma XSD ou d'un fichier XML vers du code C # flexible ou Visual Basic.
  • Support Framework 2.0 à 4.x
  • Prise en charge de la collection typée forte (List, ObservableCollection, MyCustomCollection).
  • Prend en charge les propriétés automatiques.
  • Générer des méthodes de lecture et d'écriture XML (sérialisation / désérialisation).
  • Prise en charge de la liaison de données (WPF, Xamarin).
  • WCF (attribut DataMember).
  • Prise en charge du codage XML (UTF-8/32, ASCII, Unicode, personnalisé).
  • Support de cas Camel / Pascal Case.
  • prise en charge des restrictions ([StringLengthAttribute = true / false], [RegularExpressionAttribute = true / false], [RangeAttribute = true / false]).
  • Prend en charge les fichiers XSD volumineux et complexes.
  • Prise en charge de DotNet Core et standard

0

J'ai parcouru toutes les réponses à cette date (2020-07-24), et il doit y avoir un moyen plus simple et plus familier de résoudre ce problème, qui est le suivant.

Deux scénarios ... L'un est si la chaîne XML est bien formée , c'est -à- dire qu'elle commence par quelque chose comme <?xml version="1.0" encoding="utf-16"?>ou ses semblables, avant de rencontrer l'élément racine, qui est <msg>dans la question. L'autre est s'il n'est PAS bien formé , c'est-à-dire uniquement l'élément racine (par exemple <msg>dans la question) et ses nœuds enfants uniquement.

Tout d'abord, juste une classe simple qui contient les propriétés qui correspondent, dans les noms insensibles à la casse, aux nœuds enfants du nœud racine dans le XML. Donc, d'après la question, ce serait quelque chose comme ...

public class TheModel
{
    public int Id { get; set; }
    public string Action { get; set; }
}

Ce qui suit est le reste du code ...

// These are the key using statements to add.
using Newtonsoft.Json;
using System.Xml;

bool isWellFormed = false;
string xml =  = @"
<msg>
   <id>1</id>
   <action>stop</action>
</msg>
";

var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
if (isWellFormed)
{
    xmlDocument.RemoveChild(xmlDocument.FirstChild); 
    /* i.e. removing the first node, which is the declaration part. 
    Also, if there are other unwanted parts in the XML, 
    write another similar code to locate the nodes 
    and remove them to only leave the desired root node 
    (and its child nodes).*/
}

var serializedXmlNode = JsonConvert.SerializeXmlNode(
            xmlDocument, 
            Newtonsoft.Json.Formatting.Indented, 
            true
            );
var theDesiredObject = JsonConvert.DeserializeObject<TheModel>(serializedXmlNode);

-7
public string Serialize<T>(T settings)
{
    XmlSerializer serializer = new XmlSerializer(typeof(T));
    StringWriter outStream = new StringWriter();
    serializer.Serialize(outStream, settings);
    return outStream.ToString();
}

5
C'est comment sérialiser, pas comment désérialiser.
alexc95

1
Vous venez d'écrire du code ici. Sans explication, cela n'a aucun sens pour beaucoup.
M. Haché le

Le code ne supprime pas les flux
bigfoot
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.