105 votes

Sérialisation d'un objet au format XML UTF-8 dans .NET

L'élimination appropriée des objets a été supprimée pour des raisons de brièveté, mais je suis choqué s'il s'agit du moyen le plus simple de coder un objet au format UTF-8 en mémoire. Il doit y avoir un moyen plus facile n'est-ce pas?

 var serializer = new XmlSerializer(typeof(SomeSerializableObject));

var memoryStream = new MemoryStream();
var streamWriter = new StreamWriter(memoryStream, System.Text.Encoding.UTF8);

serializer.Serialize(streamWriter, entry);

memoryStream.Seek(0, SeekOrigin.Begin);
var streamReader = new StreamReader(memoryStream, System.Text.Encoding.UTF8);
var utf8EncodedXml = streamReader.ReadToEnd();
 

263voto

Jon Skeet Points 692016

Non, vous pouvez utiliser un StringWriter pour se débarrasser de l'intermédiaire MemoryStream. Cependant, à force d'en XML, vous devez utiliser un StringWriter qui remplace l' Encoding de la propriété:

public class Utf8StringWriter : StringWriter
{
    public override Encoding Encoding
    {
         get { return Encoding.UTF8; }
    }
}

Alors:

var serializer = new XmlSerializer(typeof(SomeSerializableObject));
string utf8;
using (StringWriter writer = new Utf8StringWriter())
{
    serializer.Serialize(writer, entry);
    utf8 = writer.ToString();
}

Évidemment, vous pouvez faire Utf8StringWriter dans une classe plus générale qui accepte n'importe quel encodage dans son constructeur - mais dans mon expérience de l'UTF-8 est de loin le plus couramment requis "personnalisé" codant pour un StringWriter :)

Maintenant que Jon Hanna dit, ce sera toujours UTF-16 en interne, mais sans doute que vous allez passer à autre chose, à un certain point, de convertir en données binaires... à que point vous pouvez utiliser le haut de la chaîne, de le convertir en UTF-8 octets, et tout sera bien, parce que la déclaration XML précisera "utf-8" comme l'encodage.

EDIT: UN bref mais complet exemple pour montrer ce travail:

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

public class Test
{    
    public int X { get; set; }

    static void Main()
    {
        Test t = new Test();
        var serializer = new XmlSerializer(typeof(Test));
        string utf8;
        using (StringWriter writer = new Utf8StringWriter())
        {
            serializer.Serialize(writer, t);
            utf8 = writer.ToString();
        }
        Console.WriteLine(utf8);
    }


    public class Utf8StringWriter : StringWriter
    {
        public override Encoding Encoding
        {
            get { return Encoding.UTF8; }
        }
    }
}

Résultat:

<?xml version="1.0" encoding="utf-8"?>
<Test xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <X>0</X>
</Test>

Note de la déclaration de l'encodage "utf-8" qui est ce que nous voulions, je crois.

52voto

Jon Hanna Points 40291

Votre code n'obtient pas l'UTF-8 dans la mémoire comme vous le lire en une chaîne de nouveau, donc il n'est plus en UTF-8, mais de retour en UTF-16 (même si, idéalement, de son mieux pour considérer des chaînes de caractères à un niveau plus élevé que n'importe quel encodage, sauf quand ils sont forcés de le faire).

Pour obtenir le véritable UTF-8 octets, vous pouvez utiliser:

var serializer = new XmlSerializer(typeof(SomeSerializableObject));

var memoryStream = new MemoryStream();
var streamWriter = new StreamWriter(memoryStream, System.Text.Encoding.UTF8);

serializer.Serialize(streamWriter, entry);

byte[] utf8EncodedXml = memoryStream.ToArray();

J'ai laissé de côté la même disposition que vous avez quitté. J'ai légèrement favorable à la suivante (avec un écoulement normal à gauche):

var serializer = new XmlSerializer(typeof(SomeSerializableObject));
using(var memStm = new MemoryStream())
using(var  xw = XmlWriter.Create(memStm))
{
  serializer.Serialize(xw, entry);
  var utf8 = memStm.ToArray();
}

Ce qui est bien la même quantité de complexité, mais ne montrent que, à chaque étape, il y a un choix raisonnable de faire quelque chose d'autre, les plus pressants qui est à serialise à quelque part d'autre que de la mémoire, tel qu'un fichier, TCP/IP, des flux, de la base de données, etc. Dans l'ensemble, il n'est pas vraiment bavard.

17voto

Sebastian Castaldi Points 2138

Très bonne réponse en utilisant l'héritage, n'oubliez pas de remplacer l'initialiseur

 public class Utf8StringWriter : StringWriter
{
    public Utf8StringWriter(StringBuilder sb) : base (sb)
    {
    }
    public override Encoding Encoding { get { return Encoding.UTF8; } }
}
 

5voto

Dave Andersen Points 1822

J'ai trouvé ce blog qui explique le problème très bien, et définit quelques solutions:

Conseil: la Force de l'utf-8 ou autre encodage pour XmlWriter avec StringBuilder

J'ai opté pour l'idée que la meilleure façon de le faire est d'omettre la déclaration XML lorsque dans la mémoire. Il en fait est de l'UTF-16, à ce point de toute façon, mais la déclaration XML ne semble pas significative jusqu'à ce qu'il a été écrit dans un fichier avec un codage particulier; et même alors, la déclaration n'est pas requise. Il ne semble pas casser la désérialisation, au moins.

@Jon Hanna mentionne, ce qui peut être fait avec un XmlWriter créé comme ceci:

XmlWriter writer = XmlWriter.Create (output, new XmlWriterSettings() { OmitXmlDeclaration = true });

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X