web-dev-qa-db-fra.com

Comment renvoyer xml en UTF-8 au lieu d'UTF-16

J'utilise une routine qui sérialise <T>. Cela fonctionne, mais lorsqu'il est téléchargé sur le navigateur, je vois une page vierge. Je peux voir la source de la page ou ouvrir le téléchargement dans un éditeur de texte et je vois le xml, mais c'est en UTF-16 qui, je pense, est la raison pour laquelle les pages du navigateur s'affichent vides?

Comment puis-je modifier ma routine de sérialisation pour retourner UTF-8 au lieu de UTF-16?

La source XML a renvoyé:

<?xml version="1.0" encoding="utf-16"?>
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <string>January</string>
  <string>February</string>
  <string>March</string>
  <string>April</string>
  <string>May</string>
  <string>June</string>
  <string>July</string>
  <string>August</string>
  <string>September</string>
  <string>October</string>
  <string>November</string>
  <string>December</string>
  <string />
</ArrayOfString>

Un exemple d'appel au sérialiseur:

DateTimeFormatInfo dateTimeFormatInfo = new DateTimeFormatInfo();
var months = dateTimeFormatInfo.MonthNames.ToList();

string SelectionId = "1234567890";

return new XmlResult<List<string>>(SelectionId)
{
    Data = months
};

Le sérialiseur:

public class XmlResult<T> : ActionResult
{
    private string filename = DateTime.Now.ToString("ddmmyyyyhhss");

    public T Data { private get; set; }

    public XmlResult(string selectionId = "")
    {
        if (selectionId != "")
        {
            filename = selectionId;
        }
    }

    public override void ExecuteResult(ControllerContext context)
    {
        HttpContextBase httpContextBase = context.HttpContext;
        httpContextBase.Response.Buffer = true;
        httpContextBase.Response.Clear();

        httpContextBase.Response.AddHeader("content-disposition", "attachment; filename=" + filename + ".xml");
        httpContextBase.Response.ContentType = "text/xml";

        using (StringWriter writer = new StringWriter())
        {
            XmlSerializer xml = new XmlSerializer(typeof(T));
            xml.Serialize(writer, Data);
            httpContextBase.Response.Write(writer);
        }
    }
}
15
rwkiii

Encodage de la réponse

Je ne connais pas très bien cette partie du cadre. Mais selon le MSDN, vous pouvez définir le encodage de contenu d'une HttpResponse comme ceci:

httpContextBase.Response.ContentEncoding = Encoding.UTF8;

Encodage vu par XmlSerializer

Après avoir relu votre question, je vois que c'est la partie difficile. Le problème réside dans l'utilisation de StringWriter. Étant donné que les chaînes .NET sont toujours stockées au format UTF-16 (citation nécessaire ^^), le StringWriter renvoie cela comme son encodage. Ainsi, le XmlSerializer écrit la déclaration XML comme

<?xml version="1.0" encoding="utf-16"?>

Pour contourner ce problème, vous pouvez écrire dans un MemoryStream comme ceci:

using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream, Encoding.UTF8))
{
    XmlSerializer xml = new XmlSerializer(typeof(T));
    xml.Serialize(writer, Data);

    // I am not 100% sure if this can be optimized
    httpContextBase.Response.BinaryWrite(stream.ToArray());
}

Autres approches

Une autre modification: je viens de remarquer cette SO liée par jtm001. La solution condensée consiste à fournir au XmlSerializer un XmlWriter qui est configuré pour utiliser UTF8 comme encodage.

Athari propose pour dériver du StringWriter et annoncer l'encodage en UTF8.

À ma connaissance, les deux solutions devraient également fonctionner. Je pense que le point à retenir ici est que vous aurez besoin d'un type de code passe-partout ou d'un autre ...

7
NobodysNightmare

Vous pouvez utiliser un StringWriter qui forcera UTF8. Voici une façon de procéder:

public class Utf8StringWriter : StringWriter
{
    // Use UTF8 encoding but write no BOM to the wire
    public override Encoding Encoding
    {
         get { return new UTF8Encoding(false); } // in real code I'll cache this encoding.
    }
}

puis utilisez l'écrivain Utf8StringWriter dans votre code.

using (StringWriter writer = new Utf8StringWriter())
{
    XmlSerializer xml = new XmlSerializer(typeof(T));
    xml.Serialize(writer, Data);
    httpContextBase.Response.Write(writer);
}

la réponse est inspirée par Sérialisation d'un objet au format XML UTF-8 dans .NET

21
Yishai Galatzer

Pour sérialiser en tant que chaîne UTF8:

    private string Serialize(MyData data)
    {
        XmlSerializer ser = new XmlSerializer(typeof(MyData));
        // Using a MemoryStream to store the serialized string as a byte array, 
        // which is "encoding-agnostic"
        using (MemoryStream ms = new MemoryStream())
            // Few options here, but remember to use a signature that allows you to 
            // specify the encoding  
            using (XmlTextWriter tw = new XmlTextWriter(ms, Encoding.UTF8)) 
            {
                tw.Formatting = Formatting.Indented;
                ser.Serialize(tw, data);
                // Now we get the serialized data as a string in the desired encoding
                return Encoding.UTF8.GetString(ms.ToArray());
            }
    }

Pour le renvoyer au format XML dans une réponse Web, n'oubliez pas de définir le codage de la réponse:

    string xml = Serialize(data);
    Response.ContentType = "application/xml";
    Response.ContentEncoding = System.Text.Encoding.UTF8;
    Response.Output.Write(xml);
2
GBU