[c#] .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();



답변

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

이는 거의 동일한 복잡성이지만 모든 단계에서 다른 작업을 수행 할 합리적인 선택이 있음을 보여줍니다. 가장 시급한 것은 파일, TCP / IP와 같은 메모리가 아닌 다른 곳으로 직렬화하는 것입니다. 스트림, 데이터베이스 등. 대체로 그렇게 장황하지 않습니다.


답변

아니요, a 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🙂

이제 Jon Hanna가 말했듯이 이것은 내부적으로 여전히 UTF-16이지만, 아마도 당신은 그것을 이진 데이터로 변환하기 위해 어떤 시점에서 다른 것에 전달할 것입니다 … 시점에서 위의 문자열을 사용할 수 있습니다. 이를 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″의 선언 된 인코딩에 주목하십시오.


답변

상속을 사용하는 아주 좋은 대답입니다. 이니셜 라이저를 재정의하는 것을 잊지 마십시오.

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


답변

문제를 매우 잘 설명하고 몇 가지 다른 솔루션을 정의하는이 블로그 게시물을 찾았습니다.

(데드 링크 제거됨)

이를 수행하는 가장 좋은 방법은 메모리에있을 때 XML 선언을 완전히 생략하는 것이라고 생각했습니다. 어쨌든 실제로 그 시점에서 UTF-16이지만 XML 선언은 특정 인코딩을 사용하여 파일에 기록 될 때까지 의미가 없어 보입니다. 그리고 선언도 필요하지 않습니다. 적어도 deserialization을 깨뜨리지 않는 것 같습니다.

@Jon Hanna가 언급했듯이 다음과 같이 만든 XmlWriter로이 작업을 수행 할 수 있습니다.

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


답변