.NET에서 객체를 UTF-8 XML로 직렬화


112

간결성을 위해 적절한 개체 처리가 제거되었지만 이것이 메모리에서 개체를 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();


1
혼란 스러워요 ... 기본 인코딩 UTF-8이 아닌가요?
flq

@flq, 예, 기본값은 UTF-8이지만 다시 문자열로 다시 읽으므로 utf8EncodedXmlUTF-16 이므로 그다지 중요하지 않습니다 .
Jon Hanna

1
@Garry, Jon Skeet와 나는 다른 질문에 답하고 있기 때문에 명확히 할 수 있습니까? 객체를 UTF-8로 직렬화 하시겠습니까, 아니면 자신을 UTF-8로 선언하여 나중에 UTF-8로 인코딩 할 때 올바른 선언을 갖는 XML 문자열을 원하십니까? (이 경우 가장 간단한 방법은 UTF-8과 UTF-16 모두에 유효하기 때문에 선언을하지 않는 것입니다).
Jon Hanna

@Jon Reading back, 내 질문에 모호성이 있습니다. 주로 디버깅 목적으로 문자열로 출력했습니다. 실제로 나는 디스크 또는 HTTP를 통해 바이트를 스트리밍하여 내 문제와 더 직접적으로 관련이 있습니다. 내가 가진 주요 문제는 XML에서 UTF-8을 선언하는 것이었지만 더 정확하기 위해 플랫폼에 따라 UTF-8 바이트를 전송 / 지속하도록 문자열의 중개자를 피해야합니다. 부호화.
Garry Shutler 2010 년

답변:


55

코드는 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와 같은 메모리가 아닌 다른 곳으로 직렬화하는 것입니다. 스트림, 데이터베이스 등. 대체로 그렇게 장황하지 않습니다.


4
또한. BOM을 억제하려면을 사용할 수 있습니다 XmlWriter.Create(memoryStream, new XmlWriterSettings { Encoding = new UTF8Encoding(false) }).
ony

저와 같은 누군가가 Jon이 보여준 것처럼 생성 된 XML을 읽어야하는 경우 메모리 스트림을 0으로 재배치해야합니다. 그렇지 않으면 "Root element is missing"이라는 예외가 발생합니다. 이렇게하세요 : memStm.Position = 0; XmlReader에서의 XMLReader = XmlReader.Create (memStm)
Sudhanshu 슈라

276

아니요, 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"의 선언 된 인코딩에 주목하십시오.


2
StringWriter의 Encoding 매개 변수를 재정의하더라도 작성된 데이터를 StringBuilder로 전송하므로 여전히 UTF-16입니다. 그리고 문자열은 UTF-16 만 가능합니다.
Jon Hanna

3
@Jon : 시도해 보셨습니까? 나는 가지고 있고 작동합니다. 그것은의 선언 여기서 중요한 인코딩; 분명히 내부적으로 문자열은 여전히 ​​UTF-16이지만 바이너리 (UTF-8을 포함한 모든 인코딩을 사용할 수 있음)로 변환 될 때까지 차이가 ​​없습니다. 이 TextWriter.Encoding속성은 XML serializer에서 문서 자체 내에서 지정할 인코딩 이름을 결정하는 데 사용됩니다.
Jon Skeet

2
@Jon : 선언 된 인코딩은 무엇입니까? 내 경험상, 이것이 바로 이와 같은 질문이 실제로 시도하는 것입니다. UTF-8로 선언 된 XML 문서를 만듭니다. 당신이 말했듯이, 당신이 필요로 할 때까지 텍스트를 어떤 인코딩 으로든 고려하지 않는 것이 가장 좋습니다 . 그러나 XML 문서 가 인코딩을 선언 하기 때문에 그것은 당신이 고려해야 할 것입니다.
Jon Skeet

2
@Garry, 지금 당장 생각할 수있는 가장 간단한 것은 내 대답에서 두 번째 예제를 사용하는 것이지만 XmlWriter, XmlWriterSettings객체 를 취하고 OmitXmlDeclaration속성을 true.
Jon Hanna

4
+1 귀하의 Utf8StringWriter솔루션은 매우 친절하고 깨끗한
아드리아누 Carneiro

17

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

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

덕분에, 나는 옵션의 가장 우아한 수 있도록이 찾을
Prokurors

5

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

(데드 링크 제거됨)

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

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

XmlWriter writer = XmlWriter.Create (output, new XmlWriterSettings() { OmitXmlDeclaration = true });
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.