Sérialiser un objet en XML

292

J'ai une classe C # dont j'ai hérité. J'ai réussi à "construire" l'objet. Mais je dois sérialiser l'objet en XML. Existe-t-il un moyen simple de le faire?

Il semble que la classe ait été configurée pour la sérialisation, mais je ne sais pas comment obtenir la représentation XML. Ma définition de classe ressemble à ceci:

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.domain.com/test")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://www.domain.com/test", IsNullable = false)]
public partial class MyObject
{
  ...
}

Voici ce que je pensais pouvoir faire, mais cela ne fonctionne pas:

MyObject o = new MyObject();
// Set o properties
string xml = o.ToString();

Comment obtenir la représentation XML de cet objet?

user462166
la source
Veuillez également voir: Sérialisation et désérialisation XML sur CodeProject
Behzad Ebrahimi
1
J'ai développé une bibliothèque simple pour y parvenir: github.com/aishwaryashiva/SaveXML
Aishwarya Shiva

Réponses:

510

Vous devez utiliser XmlSerializer pour la sérialisation XML. Voici un exemple d'extrait.

 XmlSerializer xsSubmit = new XmlSerializer(typeof(MyObject));
 var subReq = new MyObject();
 var xml = "";

 using(var sww = new StringWriter())
 {
     using(XmlWriter writer = XmlWriter.Create(sww))
     {
         xsSubmit.Serialize(writer, subReq);
         xml = sww.ToString(); // Your XML
     }
 }
RameshVel
la source
10
Semble fonctionner parfaitement bien sans la ligneXmlWriter writer = XmlWriter.Create(sww);
Paul Hunt
15
Pour avoir un objet sérialisé formaté, faites: XmlTextWriter writer = new XmlTextWriter(sww) { Formatting = Formatting.Indented };au lieu deXmlWriter writer = XmlWriter.Create(sww);
Tono Nam
4
Depuis XmlWriterencapsule le, StringWritervous n'avez pas besoin de disposer des deux (la première utilisation est redondante), non? Je suppose qu'il XmlWriterprend soin de s'en débarrasser ...
talles
4
@talles n'encapsule XmlWriterpas le StringWriter, il utilise votre passé StringWriteret n'a aucune attente / responsabilité de s'en débarrasser. En outre StringWriterest en dehors de XmlWriterla portée de, vous pouvez toujours le vouloir lorsqu'il XmlWriterest éliminé, ce serait un mauvais comportement XmlWriterde disposer de votre StringWriter. En règle générale, si vous déclarez quelque chose qui doit être éliminé, vous êtes responsable de l'éliminer. Et implicitement à cette règle, tout ce que vous ne déclarez pas, vous ne devriez pas en disposer. Les deux usings sont donc nécessaires.
Arkaine55
3
using System.Xml.Serialization; using System.IO; using System.Xml;
timothy
122

J'ai modifié le mien pour retourner une chaîne plutôt que d'utiliser une variable ref comme ci-dessous.

public static string Serialize<T>(this T value)
{
    if (value == null)
    {
        return string.Empty;
    }
    try
    {
        var xmlserializer = new XmlSerializer(typeof(T));
        var stringWriter = new StringWriter();
        using (var writer = XmlWriter.Create(stringWriter))
        {
            xmlserializer.Serialize(writer, value);
            return stringWriter.ToString();
        }
    }
    catch (Exception ex)
    {
        throw new Exception("An error occurred", ex);
    }
}

Son utilisation serait la suivante:

var xmlString = obj.Serialize();
Kwex
la source
8
très belle solution, j'aime la façon dont vous avez implémenté cela comme méthode d'extension
Spyros
57
Une chose que je suggérerais ici: supprimer le bloc try ... catch. Cela ne vous donne aucun avantage et masque simplement l'erreur qui est lancée.
jammycakes
7
N'avez-vous pas également besoin d'utiliser le stringwriter? par exemple: en utilisant (var stringWriter = new StringWriter ())
Steven Quick
3
@jammycakes Non! Lorsque vous en lancez un nouveau Exception, vous avez étendu le StackTrace avec la méthode "Serialize <>".
user11909
1
@ user2190035 sûrement, s'il venait à casser la méthode d'extension, la trace de la pile commencerait là? "Étendre la trace de la pile" avec l'essai ne semble pas nécessaire?
LeRoi
43

La fonction suivante peut être copiée dans n'importe quel objet pour ajouter une fonction d'enregistrement XML à l'aide de l'espace de noms System.Xml.

/// <summary>
/// Saves to an xml file
/// </summary>
/// <param name="FileName">File path of the new xml file</param>
public void Save(string FileName)
{
    using (var writer = new System.IO.StreamWriter(FileName))
    {
        var serializer = new XmlSerializer(this.GetType());
        serializer.Serialize(writer, this);
        writer.Flush();
    }
}

Pour créer l'objet à partir du fichier enregistré, ajoutez la fonction suivante et remplacez [ObjectType] par le type d'objet à créer.

/// <summary>
/// Load an object from an xml file
/// </summary>
/// <param name="FileName">Xml file name</param>
/// <returns>The object created from the xml file</returns>
public static [ObjectType] Load(string FileName)
{
    using (var stream = System.IO.File.OpenRead(FileName))
    {
        var serializer = new XmlSerializer(typeof([ObjectType]));
        return serializer.Deserialize(stream) as [ObjectType];
    }
}
Ben Gripka
la source
writer.Flush()est redondant dans un usingbloc - writerla Dispose()méthode la videra pour vous.
Bavaza
6
Mon expérience a révélé que ce n'est pas vrai. Avec des données plus volumineuses, l'instruction using supprimera le flux avant que le tampon ne soit effacé. Je recommande à 100% d'appeler explicitement flush.
Ben Gripka
6
writer.Flush () n'est PAS redondant, il DOIT être là. Sans Flush, il peut arriver qu'une partie des données se trouve toujours dans le tampon StreamWriter et que le fichier soit supprimé et que certaines données soient manquantes.
Tomas Kubes
J'aime beaucoup votre code: court et soigné. Mon problème est de copier encore et encore les fonctions dans différentes classes: n'est-ce pas la duplication de code? Les autres réponses suggèrent une bibliothèque générique avec des méthodes d'extension de modèle, que j'embrasserais. Qu'est-ce que tu penses?
Michael G
33

Classe d'extension:

using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace MyProj.Extensions
{
    public static class XmlExtension
    {
        public static string Serialize<T>(this T value)
        {
            if (value == null) return string.Empty;

            var xmlSerializer = new XmlSerializer(typeof(T));

            using (var stringWriter = new StringWriter())
            {
                using (var xmlWriter = XmlWriter.Create(stringWriter,new XmlWriterSettings{Indent = true}))
                {
                    xmlSerializer.Serialize(xmlWriter, value);
                    return stringWriter.ToString();
                }    
            }
        }
    }
}

Usage:

Foo foo = new Foo{MyProperty="I have been serialized"};

string xml = foo.Serialize();

Il suffit de faire référence à l'espace de noms tenant votre méthode d'extension dans le fichier que vous souhaitez utiliser et ça va marcher (dans mon exemple , il serait: using MyProj.Extensions;)

Notez que si vous souhaitez rendre la méthode d'extension spécifique à une classe particulière (par exemple, Foo), vous pouvez remplacer l' Targument dans la méthode d'extension, par exemple.

public static string Serialize(this Foo value){...}

Aleksandr Albert
la source
31

Vous pouvez utiliser la fonction comme ci-dessous pour obtenir du XML sérialisé à partir de n'importe quel objet.

public static bool Serialize<T>(T value, ref string serializeXml)
{
    if (value == null)
    {
        return false;
    }
    try
    {
        XmlSerializer xmlserializer = new XmlSerializer(typeof(T));
        StringWriter stringWriter = new StringWriter();
        XmlWriter writer = XmlWriter.Create(stringWriter);

        xmlserializer.Serialize(writer, value);

        serializeXml = stringWriter.ToString();

        writer.Close();
        return true;
    }
    catch (Exception ex)
    {
        return false;
    }
}

Vous pouvez appeler cela depuis le client.

Imrul
la source
21

Pour sérialiser un objet, procédez comme suit:

 using (StreamWriter myWriter = new StreamWriter(path, false))
 {
     XmlSerializer mySerializer = new XmlSerializer(typeof(your_object_type));
     mySerializer.Serialize(myWriter, objectToSerialize);
 }

Souvenez-vous également que pour que XmlSerializer fonctionne, vous avez besoin d'un constructeur sans paramètre.

Rox
la source
2
Cela me rendait fou. Je ne pouvais pas comprendre pourquoi c'était toujours vide. Puis j'ai réalisé que je n'avais pas de constructeur sans paramètres après avoir lu votre réponse. Je vous remercie.
Andy
19

Je vais commencer par la copie de la réponse de Ben Gripka:

public void Save(string FileName)
{
    using (var writer = new System.IO.StreamWriter(FileName))
    {
        var serializer = new XmlSerializer(this.GetType());
        serializer.Serialize(writer, this);
        writer.Flush();
    }
}

J'ai utilisé ce code plus tôt. Mais la réalité a montré que cette solution est un peu problématique. Habituellement, la plupart des programmeurs se contentent de sérialiser les paramètres lors de l'enregistrement et de désérialiser les paramètres lors du chargement. Il s'agit d'un scénario optimiste. Une fois que la sérialisation a échoué, pour une raison quelconque, le fichier est partiellement écrit, le fichier XML n'est pas complet et il n'est pas valide. Par conséquent, la désérialisation XML ne fonctionne pas et votre application peut se bloquer au démarrage. Si le fichier n'est pas énorme, je suggère d'abord de sérialiser l'objet pour MemoryStreamensuite écrire le flux dans le fichier. Ce cas est particulièrement important s'il existe une sérialisation personnalisée compliquée. Vous ne pouvez jamais tester tous les cas.

public void Save(string fileName)
{
    //first serialize the object to memory stream,
    //in case of exception, the original file is not corrupted
    using (MemoryStream ms = new MemoryStream())
    {
        var writer = new System.IO.StreamWriter(ms);    
        var serializer = new XmlSerializer(this.GetType());
        serializer.Serialize(writer, this);
        writer.Flush();

        //if the serialization succeed, rewrite the file.
        File.WriteAllBytes(fileName, ms.ToArray());
    }
}

La désérialisation dans le scénario du monde réel devrait compter avec un fichier de sérialisation corrompu, cela arrive parfois. La fonction de charge fournie par Ben Gripka est très bien.

public static [ObjectType] Load(string fileName)
{
    using (var stream = System.IO.File.OpenRead(fileName))
    {
        var serializer = new XmlSerializer(typeof([ObjectType]));
        return serializer.Deserialize(stream) as [ObjectType];        
    }    
}

Et il pourrait être enveloppé par un scénario de récupération. Il convient aux fichiers de paramètres ou autres fichiers qui peuvent être supprimés en cas de problème.

public static [ObjectType] LoadWithRecovery(string fileName)
{
    try
    {
        return Load(fileName);
    }
    catch(Excetion)
    {
        File.Delete(fileName); //delete corrupted settings file
        return GetFactorySettings();
    }
}
Tomas Kubes
la source
N'est-il pas possible d'interrompre le processus lors de l'écriture du MemoryStream dans un fichier, par exemple par une coupure de courant?
John Smith
1
Oui c'est possible. Vous pouvez l'éviter en écrivant les paramètres dans un fichier temporaire, puis en remplaçant l'original.
Tomas Kubes
18

Toutes les réponses votées ci-dessus sont correctes. Ceci est juste la version la plus simple:

private string Serialize(Object o)
{
    using (var writer = new StringWriter())
    {
        new XmlSerializer(o.GetType()).Serialize(writer, o);
        return writer.ToString();
    }
}
avj
la source
9

C'est un peu plus compliqué que d'appeler la ToStringméthode de la classe, mais pas beaucoup.

Voici une fonction d'insertion simple que vous pouvez utiliser pour sérialiser tout type d'objet. Il renvoie une chaîne contenant le contenu XML sérialisé:

public string SerializeObject(object obj)
{
    System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
    System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
    using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) {
        serializer.Serialize(ms, obj);
        ms.Position = 0;
        xmlDoc.Load(ms);
        return xmlDoc.InnerXml;
    }
}
Cody Gray
la source
4
    string FilePath = ConfigurationReader.FileLocation;   //Getting path value from web.config            
    XmlSerializer serializer = new XmlSerializer(typeof(Devices)); //typeof(object)
            MemoryStream memStream = new MemoryStream();
            serializer.Serialize(memStream, lstDevices);//lstdevices : I take result as a list.
            FileStream file = new FileStream(folderName + "\\Data.xml", FileMode.Create, FileAccess.ReadWrite); //foldername:Specify the path to store the xml file
            memStream.WriteTo(file);
            file.Close();

Vous pouvez créer et stocker le résultat sous forme de fichier xml à l'emplacement souhaité.

Dev Try
la source
4

mon code de travail. Renvoie utf8 xml active l' espace de noms vide.

// override StringWriter
public class Utf8StringWriter : StringWriter
{
    public override Encoding Encoding => Encoding.UTF8;
}

private string GenerateXmlResponse(Object obj)
{    
    Type t = obj.GetType();

    var xml = "";

    using (StringWriter sww = new Utf8StringWriter())
    {
        using (XmlWriter writer = XmlWriter.Create(sww))
        {
            var ns = new XmlSerializerNamespaces();
            // add empty namespace
            ns.Add("", "");
            XmlSerializer xsSubmit = new XmlSerializer(t);
            xsSubmit.Serialize(writer, obj, ns);
            xml = sww.ToString(); // Your XML
        }
    }
    return xml;
}

Exemple de retour de réponse Yandex api payment Aviso url:

<?xml version="1.0" encoding="utf-8"?><paymentAvisoResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" performedDatetime="2017-09-01T16:22:08.9747654+07:00" code="0" shopId="54321" invoiceId="12345" orderSumAmount="10643" />
dev-siberia
la source
4

J'ai un moyen simple de sérialiser un objet en XML en utilisant C #, cela fonctionne très bien et il est hautement réutilisable. Je sais que c'est un fil plus ancien, mais je voulais le publier car quelqu'un pourrait le trouver utile.

Voici comment j'appelle la méthode:

var objectToSerialize = new MyObject();
var xmlString = objectToSerialize.ToXmlString();

Voici la classe qui fait le travail:

Remarque: Comme il s'agit de méthodes d'extension, elles doivent être dans une classe statique.

using System.IO;
using System.Xml.Serialization;

public static class XmlTools
{
    public static string ToXmlString<T>(this T input)
    {
        using (var writer = new StringWriter())
        {
            input.ToXml(writer);
            return writer.ToString();
        }
    }

    private static void ToXml<T>(this T objectToSerialize, StringWriter writer)
    {
        new XmlSerializer(typeof(T)).Serialize(writer, objectToSerialize);
    }
}
Tyler Kalosza
la source
4

Basé sur les solutions ci-dessus, voici une classe d'extension que vous pouvez utiliser pour sérialiser et désérialiser n'importe quel objet. Toute autre attribution XML dépend de vous.

Utilisez-le simplement comme ceci:

        string s = new MyObject().Serialize(); // to serialize into a string
        MyObject b = s.Deserialize<MyObject>();// deserialize from a string



internal static class Extensions
{
    public static T Deserialize<T>(this string value)
    {
        var xmlSerializer = new XmlSerializer(typeof(T));

        return (T)xmlSerializer.Deserialize(new StringReader(value));
    }

    public static string Serialize<T>(this T value)
    {
        if (value == null)
            return string.Empty;

        var xmlSerializer = new XmlSerializer(typeof(T));

        using (var stringWriter = new StringWriter())
        {
            using (var xmlWriter = XmlWriter.Create(stringWriter, new XmlWriterSettings { Indent = true }))
            {
                xmlSerializer.Serialize(xmlWriter, value);
                return stringWriter.ToString();
            }
        }
    }
}
Hefaistos68
la source
2

Ou vous pouvez ajouter cette méthode à votre objet:

    public void Save(string filename)
    {
        var ser = new XmlSerializer(this.GetType());
        using (var stream = new FileStream(filename, FileMode.Create))
            ser.Serialize(stream, this);
    }
Bigjim
la source
1

Voici un code de base qui aidera à sérialiser les objets C # en XML:

using System;

public class clsPerson
{
  public  string FirstName;
  public  string MI;
  public  string LastName;
}

class class1
{ 
   static void Main(string[] args)
   {
      clsPerson p=new clsPerson();
      p.FirstName = "Jeff";
      p.MI = "A";
      p.LastName = "Price";
      System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(p.GetType());
      x.Serialize(Console.Out, p);
      Console.WriteLine();
      Console.ReadLine();
   }
}    
Ali Asad
la source
6
Ce serait bien si vous citez la source de ce code: support.microsoft.com/en-us/help/815813/…
MaLiN2223
0
public string ObjectToXML(object input)
{
    try
    {
        var stringwriter = new System.IO.StringWriter();
        var serializer = new XmlSerializer(input.GetType());
        serializer.Serialize(stringwriter, input);
        return stringwriter.ToString();
    }
    catch (Exception ex)
    {
        if (ex.InnerException != null)
            ex = ex.InnerException;

        return "Could not convert: " + ex.Message;
    }
}

//Usage
var res = ObjectToXML(obj)

Vous devez utiliser les classes suivantes:

using System.IO;
using System.Xml;
using System.Xml.Serialization;
Sagar Timalsina
la source