.NET XML 직렬화 문제? [닫은]


121

C # XML 직렬화를 수행 할 때 몇 가지 문제가 발생했습니다.


using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;

[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
{      
    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(System.Xml.XmlReader reader)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

        bool wasEmpty = reader.IsEmptyElement;
        reader.Read();

        if (wasEmpty)
            return;

        while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
        {
            reader.ReadStartElement("item");

            reader.ReadStartElement("key");
            TKey key = (TKey)keySerializer.Deserialize(reader);
            reader.ReadEndElement();

            reader.ReadStartElement("value");
            TValue value = (TValue)valueSerializer.Deserialize(reader);
            reader.ReadEndElement();

            this.Add(key, value);

            reader.ReadEndElement();
            reader.MoveToContent();
        }
        reader.ReadEndElement();
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

        foreach (TKey key in this.Keys)
        {
            writer.WriteStartElement("item");

            writer.WriteStartElement("key");
            keySerializer.Serialize(writer, key);
            writer.WriteEndElement();

            writer.WriteStartElement("value");
            TValue value = this[key];
            valueSerializer.Serialize(writer, value);
            writer.WriteEndElement();

            writer.WriteEndElement();
        }
    }
}

다른 XML 직렬화 문제가 있습니까?


더 많은 gotchas 롤을 찾고, 당신은 나를 도울 수 있을지도 모릅니다 : stackoverflow.com/questions/2663836/…
— Shimmy Weitzhandler

1
또한 Charles Feduke의 직렬화 가능한 사전 구현을 살펴보고 싶을 것입니다. 그는 기본 직렬화 프로그램에 의해 직렬화 될 정규 구성원에 대한 귀속 가능한 구성원 사이에 xml 작성자를 알릴 수 있도록했습니다. deploymentzone.com/2008/09/19/…
— Shimmy Weitzhandler

이것은 모든 문제를 해결하는 것 같지 않습니다. 생성자에서 IEqualityComparer를 설정하고 있지만이 코드에서는 직렬화되지 않습니다. 이 정보를 포함하도록이 사전을 확장하는 방법에 대한 아이디어가 있습니까? 그 정보는 Type 객체를 통해 처리 될 수 있습니까?
— ColinCren

답변:


27

또 다른 큰 문제 : 웹 페이지 (ASP.NET)를 통해 XML을 출력 할 때 Unicode Byte-Order Mark 를 포함하고 싶지 않습니다 . 물론 BOM을 사용하거나 사용하지 않는 방법은 거의 동일합니다.

BAD (BOM 포함) :

XmlTextWriter wr = new XmlTextWriter(stream, new System.Text.Encoding.UTF8);

좋은:

XmlTextWriter  wr = new XmlTextWriter(stream, new System.Text.UTF8Encoding(false))

BOM을 원하지 않음을 나타 내기 위해 명시 적으로 false를 전달할 수 있습니다. 사이의 명확하고 분명한 차이점에 주목 Encoding.UTF8와 UTF8Encoding.

처음에 추가 된 3 개의 BOM 바이트는 (0xEFBBBF) 또는 (239187191)입니다.

참조 : http://chrislaco.com/blog/troubleshooting-common-problems-with-the-xmlserializer/


4
당신의 의견은 당신이 우리에게 무엇을뿐만 아니라 이유를 말하면 훨씬 더 유용 할 것입니다.
— Neil

1
이것은 정말 ... XML 직렬화에 관한 그것은 단지 XmlTextWriter의 문제입니다하지 않습니다
— 토마스 레베을

7
-1 : 질문과 관련이 없으며 XmlTextWriter.NET 2.0 이상에서 사용해서는 안됩니다 .
— John Saunders

매우 유용한 참조 링크. 감사.
— Anil Vangari 2012-06-21

21

아직 댓글을 달 수 없어서 Dr8k의 게시물에 댓글을 달고 또 다른 관찰을하겠습니다. 공용 getter / setter 속성으로 노출되고 이러한 속성을 통해 직렬화 / 역 직렬화되는 개인 변수. 우리는 항상 예전 직장에서 해냈습니다.

하지만 한 가지 주목할 점은 해당 속성에 논리가있는 경우 논리가 실행되기 때문에 직렬화 순서가 실제로 중요하다는 것입니다. 멤버는 코드에서 정렬 된 방식에 따라 암시 적으로 정렬되지만 특히 다른 개체를 상속하는 경우에는 보장되지 않습니다. 명시 적으로 주문하는 것은 후방의 고통입니다.

나는 과거에 이것에 타 버린 적이 있습니다.


17
필드 순서를 명시 적으로 설정하는 방법을 검색하는 동안이 게시물을 찾았습니다. 이것은 속성으로 수행됩니다. [XmlElementAttribute (Order = 1)] public int Field {...} 단점 : 속성은 클래스의 모든 필드와 모든 하위 항목에 대해 지정되어야합니다! IMO 당신은 이것을 당신의 포스트에 추가해야합니다.
— Cristian Diaconescu

15

메모리 스트림에서 XML 문자열로 직렬화 할 때 MemoryStream # GetBuffer () 대신 MemoryStream # ToArray ()를 사용해야합니다. 그렇지 않으면 할당 된 추가 버퍼로 인해 제대로 역 직렬화되지 않는 정크 문자로 끝납니다.

http://msdn.microsoft.com/en-us/library/system.io.memorystream.getbuffer(VS.80).aspx


3
문서에서 바로 "버퍼에는 사용되지 않을 수있는 할당 된 바이트가 포함되어 있습니다. 예를 들어"test "문자열이 MemoryStream 객체에 기록되면 GetBuffer에서 반환 된 버퍼의 길이는 252 바이트로 4가 아니라 256입니다. 사용되지 않습니다. 버퍼의 데이터 만 얻으려면 ToArray 메서드를 사용하십시오. 그러나 ToArray는 메모리에 데이터 복사본을 만듭니다. " msdn.microsoft.com/en-us/library/…
— realgt

방금 이걸 봤어요. 더 이상 말도 안되는 소리가 아닙니다.
— John Saunders

이것은 디버깅에 도움이되는 이전에 들어 본 적이 없습니다.
— Ricky

10

serializer가 유형으로 인터페이스가있는 멤버 / 속성을 발견하면 직렬화되지 않습니다. 예를 들어 다음은 XML로 직렬화되지 않습니다.

public class ValuePair
{
    public ICompareable Value1 { get; set; }
    public ICompareable Value2 { get; set; }
}

이것은 직렬화되지만 :

public class ValuePair
{
    public object Value1 { get; set; }
    public object Value2 { get; set; }
}

"구성원에 대해 유형이 확인되지 않았습니다 ..."라는 메시지와 함께 예외가 발생하면이 문제가 발생할 수 있습니다.
— Kyle Krull

9

IEnumerables<T>수익률을 통해 생성 된 것은 직렬화 할 수 없습니다. 이는 컴파일러가 yield return을 구현하기 위해 별도의 클래스를 생성하고 해당 클래스가 직렬화 가능으로 표시되지 않기 때문입니다.


이것은 '기타'직렬화, 즉 [Serializable] 속성에 적용됩니다. 그러나 XmlSerializer에서도 작동하지 않습니다.
— Tim Robinson


8

읽기 전용 속성을 직렬화 할 수 없습니다. XML을 객체로 변환하기 위해 deserialization을 사용하지 않더라도 getter와 setter가 있어야합니다.

같은 이유로 인터페이스를 반환하는 속성을 직렬화 할 수 없습니다. deserializer는 인스턴스화 할 구체적인 클래스를 알지 못합니다.


1
실제로 컬렉션 속성은 setter가 없어도 직렬화 할 수 있지만 생성자에서 초기화해야 deserialization이 항목을 추가 할 수 있습니다
— Thomas Levesque

7

오 여기 좋은 것이 있습니다. XML 직렬화 코드가 생성되어 별도의 DLL에 배치되기 때문에 코드에 직렬화를 중단시키는 실수가 있어도 의미있는 오류가 발생하지 않습니다. "s3d3fsdf.dll을 찾을 수 없음"과 같은 것입니다. 좋은.


11
XML "Serializer Generator Tool (Sgen.exe)"를 사용하여 해당 DLL을 미리 생성하고 응용 프로그램과 함께 배포 할 수 있습니다.
— huseyint

6

매개 변수없는 생성자가없는 객체는 직렬화 할 수 없습니다.

그리고 어떤 이유로 다음 속성에서 Value는 직렬화되지만 FullName은 아닙니다.

    public string FullName { get; set; }
    public double Value { get; set; }

왜 그런지 알아 내지 못했지만 Value를 내부로 변경했습니다 ...


4
매개 변수없는 생성자는 개인 / 보호 될 수 있습니다. XML serializer에 충분합니다. FullName의 문제는 정말 이상합니다. 발생해서는 안됩니다.
— Max Galkin 2009-06-17

@Yacoder : 어쩌면 때문에하지 double?하지만 단지 double?
— abatishchev 2011 년

FullName은 아마도 null직렬화 될 때 XML을 생성하지 않을 것입니다
— Jesper


4

XML Serialization 생성 어셈블리가이를 사용하려는 코드와 동일한로드 컨텍스트에 있지 않으면 다음과 같은 멋진 오류가 발생합니다.

System.InvalidOperationException: There was an error generating the XML document.
---System.InvalidCastException: Unable to cast object
of type 'MyNamespace.Settings' to type 'MyNamespace.Settings'. at
Microsoft.Xml.Serialization.GeneratedAssembly.
  XmlSerializationWriterSettings.Write3_Settings(Object o)

이 문제의 원인은 로드 컨텍스트를 사용하는 데 많은 단점이있는 LoadFrom 컨텍스트 를 사용하여로드 된 플러그인이었습니다 . 그것을 추적하는 것은 꽤 재미 있습니다.




4

당신이 배열 직렬화하려고하면 List<T>, 또는 IEnumerable<T>인스턴스가 들어 서브 클래스 의를 T, 당신은 사용할 필요가 XmlArrayItemAttribute 목록에 대한 모든 하위 유형이 사용합니다. 그렇지 않으면 System.InvalidOperationException직렬화 할 때 런타임에 도움이되지 않습니다.

다음은 문서 의 전체 예제 중 일부입니다.

public class Group
{  
   /* The XmlArrayItemAttribute allows the XmlSerializer to insert both the base 
      type (Employee) and derived type (Manager) into serialized arrays. */

   [XmlArrayItem(typeof(Manager)), XmlArrayItem(typeof(Employee))]
   public Employee[] Employees;

3

개인 변수 / 속성은 XML 직렬화의 기본 메커니즘에서 직렬화되지 않지만 이진 직렬화에 있습니다.


2
예, "기본"XML 직렬화를 사용하는 경우입니다. 클래스에서 IXmlSerializable을 구현하는 사용자 지정 XML 직렬화 논리를 지정하고 필요 / 원하는 모든 개인 필드를 직렬화 할 수 있습니다.
— Max Galkin

1
음, 사실입니다. 나는 이것을 편집 할 것이다. 그러나 그 인터페이스를 구현하는 것은 내가 기억하는 것에서 일종의 고통입니다.
— Charles Graham

3

속성으로 표시된 Obsolete속성은 직렬화되지 않습니다. Deprecated속성으로 테스트하지 않았지만 동일한 방식으로 작동 할 것이라고 가정합니다.


2

나는 이것을 정말로 설명 할 수 없지만 이것이 직렬화되지 않는다는 것을 알았다.

[XmlElement("item")]
public myClass[] item
{
    get { return this.privateList.ToArray(); }
}

그러나 이것은 :

[XmlElement("item")]
public List<myClass> item
{
    get { return this.privateList; }
}

또한 memstream으로 직렬화하는 경우 사용하기 전에 0을 찾는 것이 좋습니다.


재건 할 수 없기 때문이라고 생각합니다. 두 번째 예제에서는 item.Add ()를 호출하여 항목을 목록에 추가 할 수 있습니다. 처음에는 할 수 없습니다.
— ilitirit

18
사용 : [XmlArray ( "item"), XmlArrayItem ( "myClass", typeof (myClass))]
— RvdK

1
건배! 매일 무언가를 배우십시오
— annakata


2

XSD가 대체 그룹을 사용하는 경우 자동으로 직렬화를 해제 할 수 없습니다. 이 시나리오를 처리하려면 고유 한 직렬 변환기를 작성해야합니다.

예 :

<xs:complexType name="MessageType" abstract="true">
    <xs:attributeGroup ref="commonMessageAttributes"/>
</xs:complexType>

<xs:element name="Message" type="MessageType"/>

<xs:element name="Envelope">
    <xs:complexType mixed="false">
        <xs:complexContent mixed="false">
            <xs:element ref="Message" minOccurs="0" maxOccurs="unbounded"/>
        </xs:complexContent>
    </xs:complexType>
</xs:element>

<xs:element name="ExampleMessageA" substitutionGroup="Message">
    <xs:complexType mixed="false">
        <xs:complexContent mixed="false">
                <xs:attribute name="messageCode"/>
        </xs:complexContent>
    </xs:complexType>
</xs:element>

<xs:element name="ExampleMessageB" substitutionGroup="Message">
    <xs:complexType mixed="false">
        <xs:complexContent mixed="false">
                <xs:attribute name="messageCode"/>
        </xs:complexContent>
    </xs:complexType>
</xs:element>

이 예에서 봉투는 메시지를 포함 할 수 있습니다. 그러나 .NET의 기본 serializer는 Message, ExampleMessageA 및 ExampleMessageB를 구분하지 않습니다. 기본 Message 클래스로만 직렬화됩니다.


0

개인용 변수 / 속성은 XML 직렬화로 직렬화되지 않지만 이진 직렬화에 있습니다.

공개 속성을 통해 비공개 멤버를 노출하는 경우에도 이것이 얻을 수 있다고 생각합니다. 비공개 멤버는 직렬화되지 않으므로 공개 멤버는 모두 null 값을 참조합니다.


이것은 사실이 아닙니다. public 속성의 setter가 호출되고 아마도 private 멤버를 설정합니다.
— John Saunders
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.