web-dev-qa-db-ja.com

.NETでオブジェクトをUTF-8 XMLとしてシリアル化する

適切なオブジェクトの破棄は簡潔にするために削除されましたが、これがメモリ内でオブジェクトをUTF-8としてエンコードする最も簡単な方法である場合、私はショックを受けます。もっと簡単な方法がありませんか?

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();
101
Garry Shutler

コードは文字列に再度読み込むときにUTF-8をメモリに入れないため、UTF-8ではなくUTF-16に戻ります(理想的には、文字列をより高いレベルで考慮するのが最善です)強制的に行われる場合を除き、任意のエンコード)。

実際のUTF-8オクテットを取得するには、次を使用できます。

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

私はあなたが残したのと同じ処分を省いた。私は次のことを少し支持します(通常の処理はそのままです):

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

これはほぼ同じ量の複雑さですが、すべての段階で何か他のことを行うための合理的な選択があることを示しています。ストリーム、データベースなど。全体として、実際にはそれほど冗長ではありません。

52
Jon Hanna

いいえ、StringWriterを使用して、中間のMemoryStreamを削除できます。ただし、XMLに強制するには、StringWriterプロパティをオーバーライドするEncodingを使用する必要があります。

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

または、C#6をまだ使用していない場合:

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

次に:

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

明らかに、Utf8StringWriterをそのコンストラクターで任意のエンコーディングを受け入れるより一般的なクラスにすることができますが、私の経験では、UTF-8はStringWriterに最も一般的に必要な「カスタム」エンコーディングです。

ジョン・ハンナが言うように、これはまだ内部的にUTF-16ですが、おそらくあなたはそれをある時点で他の何かに渡し、バイナリデータに変換するでしょう... that point上記の文字列を使用して、UTF-8バイトに変換できます。XML宣言ではエンコードとして「utf-8」が指定されるため、すべてうまくいきます。

編集:この動作を示すための短いが完全な例:

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 => Encoding.UTF8;
    }
}

結果:

<?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>

「utf-8」の宣言されたエンコーディングに注意してください。

256
Jon Skeet

継承を使用した非常に良い答え、イニシャライザをオーバーライドすることを忘れないでください

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

問題を非常によく説明し、いくつかの異なるソリューションを定義しているこのブログ投稿を見つけました:

(デッドリンクが削除されました)

最善の方法は、メモリ内でXML宣言を完全に省略することだという考えに落ち着きました。とにかく実際にはis UTF-16ですが、XML宣言は、特定のエンコーディングでファイルに書き込まれるまで意味がないようです。そして、それでも宣言は必要ありません。少なくとも逆シリアル化を壊さないようです。

@Jon Hannaが言及しているように、これは次のように作成されたXmlWriterを使用して実行できます。

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