2009-08-18 3 views
60

제 3 자로부터 xml을 얻었으며이를 C# 객체로 비 직렬화해야합니다. 이 XML에는 정수 유형 값 또는 빈 값 속성이 포함될 수 있습니다. attr = "11"또는 attr = "" 이 속성 값을 Nullable 정수 형식의 속성으로 deserialize하고 싶습니다. 그러나 XmlSerializer는 Nullable 형식으로의 deserialization을 지원하지 않습니다. 다음 테스트 코드는 InvalidOperationException (""TestConsoleApplication.SerializeMe '형식을 반영하는 오류가 발생했습니다.)과 함께 XmlSerializer를 만드는 동안 실패합니다. 내가 int로 '가치'속성의 유형을 변경하는 경우XmlSerializer를 사용하여 null xml 속성 값을 nullable int 속성으로 비 직렬화

[XmlRoot("root")] 
public class SerializeMe 
{ 
    [XmlElement("element")] 
    public Element Element { get; set; } 
} 

public class Element 
{ 
    [XmlAttribute("attr")] 
    public int? Value { get; set; } 
} 

class Program { 
    static void Main(string[] args) { 
     string xml = "<root><element attr=''>valE</element></root>"; 
     var deserializer = new XmlSerializer(typeof(SerializeMe)); 
     Stream xmlStream = new MemoryStream(Encoding.ASCII.GetBytes(xml)); 
     var result = (SerializeMe)deserializer.Deserialize(xmlStream); 
    } 
} 

는, 직렬화는 InvalidOperationException이 실패 : XML 문서 (1, 16)에 오류가

.

아무도 정수로 비어 있지 않은 속성 값을 비 직렬화하는 동시에 null 값 유형 (null)로 빈 값 속성을 비 직렬화하는 방법에 대해 조언 할 수 있습니까? 거기에 어떤 트릭이 있으므로 수동으로 각 필드의 deserialization 할 필요가 없습니다 (실제로 거기에 그들 중 많은)?

업데이트 ahsteele에서 코멘트 후 :

    내가 아는 한
  1. Xsi:nil attribute

    ,이 속성은 XmlElementAttribute와 함께 작동 -이 속성은 요소에 아무 내용이 없다 지정, 자식 요소 또는 신체 여부 본문. 하지만 XmlAttributeAttribute에 대한 솔루션을 찾아야합니다. 어쨌든 xml을 제어 할 수 없기 때문에 xml을 변경할 수 없습니다.

  2. bool *Specified property

    속성 값이 비어 있지 않은 경우에만 또는 속성이없는 경우이 속성은 작동합니다. attr에 빈 값 (attr = '')이 있으면 XmlSerializer 생성자가 실패합니다 (예상대로).

  3. Custom Nullable class like in this blog post by Alex Scordellis

    public class Element 
    { 
        [XmlAttribute("attr")] 
        public int Value { get; set; } 
    
        [XmlIgnore] 
        public bool ValueSpecified; 
    } 
    
  4. 나는 내 문제에 대한이 블로그 게시물에서 클래스를 채택하려고 :

    [XmlAttribute("attr")] 
    public NullableInt Value { get; set; } 
    

    그러나 XmlSerializer를 생성자는 InvalidOperationException이 실패합니다

    직렬화 할 수 없습니다 멤버 TestConsoleApplication.NullableInt 유형의 '값'.

    있는 XmlAttribute/XMLTEXT은 (:) 내가 여기에이 코드를 쓴 나 수치) IXmlSerializable을 구현하는 유형}

  5. 미운 대리 솔루션을 인코딩하는 데 사용할 수 없습니다

    public class Element 
    { 
        [XmlAttribute("attr")] 
        public string SetValue { get; set; } 
    
        public int? GetValue() 
        { 
         if (string.IsNullOrEmpty(SetValue) || SetValue.Trim().Length <= 0) 
          return null; 
    
         int result; 
         if (int.TryParse(SetValue, out result)) 
          return result; 
    
         return null; 
        } 
    } 
    

    하지만 돈 소비자를위한 수업을 방해하기 때문에 이런 해결책을 생각해 내고 싶지 않습니다. IXmlSerializable 인터페이스를 수동으로 구현하는 것이 좋습니다.나는 전체 요소 클래스 IXmlSerializable을 구현해야처럼

은 현재 보이는 (그것은 큰)과 간단한 해결 방법 ...

답변

17

IXmlSerializable 인터페이스를 구현하여이 문제를 해결했습니다. 나는 쉬운 길을 찾지 못했다.

[XmlRoot("root")] 
public class DeserializeMe { 
    [XmlArray("elements"), XmlArrayItem("element")] 
    public List<Element> Element { get; set; } 
} 

public class Element : IXmlSerializable { 
    public int? Value1 { get; private set; } 
    public float? Value2 { get; private set; } 

    public void ReadXml(XmlReader reader) { 
     string attr1 = reader.GetAttribute("attr"); 
     string attr2 = reader.GetAttribute("attr2"); 
     reader.Read(); 

     Value1 = ConvertToNullable<int>(attr1); 
     Value2 = ConvertToNullable<float>(attr2); 
    } 

    private static T? ConvertToNullable<T>(string inputValue) where T : struct { 
     if (string.IsNullOrEmpty(inputValue) || inputValue.Trim().Length == 0) { 
      return null; 
     } 

     try { 
      TypeConverter conv = TypeDescriptor.GetConverter(typeof(T)); 
      return (T)conv.ConvertFrom(inputValue); 
     } 
     catch (NotSupportedException) { 
      // The conversion cannot be performed 
      return null; 
     } 
    } 

    public XmlSchema GetSchema() { return null; } 
    public void WriteXml(XmlWriter writer) { throw new NotImplementedException(); } 
} 

class TestProgram { 
    public static void Main(string[] args) { 
     string xml = @"<root><elements><element attr='11' attr2='11.3'/><element attr='' attr2=''/></elements></root>"; 
     XmlSerializer deserializer = new XmlSerializer(typeof(DeserializeMe)); 
     Stream xmlStream = new MemoryStream(Encoding.ASCII.GetBytes(xml)); 
     var result = (DeserializeMe)deserializer.Deserialize(xmlStream); 
    } 
} 
9

내가 말을 많이에게 직렬화 주변에 자신을 장난 봤는데이 없습니다 값 유형에 대해 널 (null) 데이터를 다룰 때 다음 기사와 게시물을 찾았습니다.

에 대한 대답 How to make a value type nullable with XmlSerializer in C# - serialization에 대한 답변은 XmlSerializer의 아주 멋진 간계입니다. 특히 XmlSerialier는 XXXSpecified 부울 속성을 검색하여 null을 무시할 수 있는지 여부를 결정해야합니다.

Alex Scordellis는 a good answer을받은 StackOverflow 질문을했습니다. Alex는 또한 자신이 해결하려고 시도했던 문제에 대한 그의 글에 좋은 글을 올렸습니다. Using XmlSerializer to deserialize into a Nullable<int>.

Xsi:nil Attribute Binding Support에 대한 MSDN 설명서도 유용합니다. IXmlSerializable Interface에 대한 문서와 마찬가지로 독자적인 구현을 작성하는 것이 최후의 수단이어야합니다.

+0

는 "null 허용으로 역 직렬화 할 XmlSerializer를 사용"을 변환과 함께 잠재적 널 (NULL) 항목을 나타 내기 위해 링크는 죽었다 . [google에서 캐시 된 버전] (http://webcache.googleusercontent.com/search?q=cache:vT5GiyOCWyIJ:www.rqna.net/qna/zzrzt-deserialise-missing-xml-attribute-to-nullable-type .html) – Anttu

+0

@Anttu XmlSerializer를 사용하여 Nullable *으로 역 직렬화하는 방법을 사용하여 원래의 Wayback Machine 아카이브에 대한 링크를 전환했습니다. – ahsteele

43

이 작동합니다 : 사용자 정의 형식을 만들어이 문제를 해결 :

[XmlIgnore] 
public int? Age { get; set; } 

[XmlElement("Age")] 
public string AgeAsText 
{ 
    get { return (Age.HasValue) ? Age.ToString() : null; } 
    set { Age = !string.IsNullOrEmpty(value) ? int.Parse(value) : default(int?); } 
} 
+4

이 작업은 가능하지만이 질문은 4 번과 같은 해결책입니다. 내 수업의 공용 인터페이스에 대리 필드를 도입하고 싶지 않습니다. 감사합니다 –

+4

FWIW,이 솔루션은 명시 적 IXmlSerializable 구현 (수락 된 솔루션)보다 나은 찾으십시오 있지만 OP 특정 질문 수 없습니다. 절대적으로 필요한 경우가 아니라면 IXmlSerializable을 구현하지 마십시오. 장기간에 걸쳐 유지 관리에 더 많은 비용이 들게됩니다. 이와 같은 간단한 사례와 다른 완화 요소가없는 상황에서 두 번째 생각을하지 않고 "추한"대리 솔루션을 찾아 보겠습니다. –

2

생각 나뿐만 아니라 모자에 내 대답을 던질 수있는 다음

는 테스트 코드 샘플입니다 여기에는 IXmlSerializable 인터페이스가 구현됩니다.

다음 노드가있는 XML 객체가 있다고 가정 해보십시오.

<ItemOne>10</Item2> 
<ItemTwo /> 

210 개체를 나타내는 :

public class MyItems { 
    [XmlElement("ItemOne")] 
    public int ItemOne { get; set; } 

    [XmlElement("ItemTwo")] 
    public CustomNullable<int> ItemTwo { get; set; } // will throw exception if empty element and type is int 
} 

동적 널 (NULL) 구조체

public struct CustomNullable<T> : IXmlSerializable where T: struct { 
    private T value; 
    private bool hasValue; 

    public bool HasValue { 
     get { return hasValue; } 
    } 

    public T Value { 
     get { return value; } 
    } 

    private CustomNullable(T value) { 
     this.hasValue = true; 
     this.value = value; 
    } 

    public XmlSchema GetSchema() { 
     return null; 
    } 

    public void ReadXml(XmlReader reader) { 
     string strValue = reader.ReadString(); 
     if (String.IsNullOrEmpty(strValue)) { 
      this.hasValue = false; 
     } 
     else { 
      T convertedValue = strValue.To<T>(); 
      this.value = convertedValue; 
      this.hasValue = true; 
     } 
     reader.ReadEndElement(); 

    } 

    public void WriteXml(XmlWriter writer) { 
     throw new NotImplementedException(); 
    } 

    public static implicit operator CustomNullable<T>(T value) { 
     return new CustomNullable<T>(value); 
    } 

} 

public static class ObjectExtensions { 
    public static T To<T>(this object value) { 
     Type t = typeof(T); 
     // Get the type that was made nullable. 
     Type valueType = Nullable.GetUnderlyingType(typeof(T)); 
     if (valueType != null) { 
      // Nullable type. 
      if (value == null) { 
       // you may want to do something different here. 
       return default(T); 
      } 
      else { 
       // Convert to the value type. 
       object result = Convert.ChangeType(value, valueType); 
       // Cast the value type to the nullable type. 
       return (T)result; 
      } 
     } 
     else { 
      // Not nullable. 
      return (T)Convert.ChangeType(value, typeof(T)); 
     } 
    } 
} 
관련 문제