ProgramingTip

XmlSerializer를 사용하여 빈 xml 속성 값을 nullable int 속성으로 역화 화

bestdevel 2020. 10. 30. 08:18
반응형

XmlSerializer를 사용하여 빈 xml 속성 값을 nullable int 속성으로 역화 화


xml을 가져 오기에서 C # 개체로 역화해야합니다. 이 xml은 정수 유형 값 또는 빈 값 (attr =”11”또는 attr =””)의 속성이 사용할 수 있습니다. 이 속성 값을 nullable 정수 유형의 속성으로 deserialize하고 싶습니다. 그러나 XmlSerializer는 nullable 형식으로의 역화를 지원하지 않습니다. InvalidOperationException { " 'TestConsoleApplication.SerializeMe'유형 반영 오류가 발생했습니다."}와 함께 XmlSerializer를 만드는 동안 다음 테스트 코드가 실패합니다.

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

'Value'속성의 유형을 int로 변경하면 InvalidOperationException으로 deserialization이 실패합니다.

XML 문서 (1, 16)에 오류가 있습니다.

아무도 비어 있지 않은 속성 값을 정수로 역 결합 화하는 동시에 빈 값을 가진 속성을 nullable 형식 (null)으로 역 배열 화하는 방법을 조언 할 수 있습니까? 이에 대한 트릭이 있으므로 각 필드를 수동으로 역 생성 화 할 필요가 없습니까 (실제로 많은 필드가 있음)?

ahsteele의 의견 후 업데이트 :

  1. Xsi : nil 속성

    내가 아는 한이 속성은 XmlElementAttribute에서만 작동합니다.이 속성은 어떤 요소 든 본문 텍스트 요소에 콘텐츠가 없음을 지정합니다. 하지만 XmlAttributeAttribute에 대한 솔루션을 찾아야합니다. 어쨌든 나는 그것을 제어 할 수 없기 때문에 xml을 사용할 수 없습니다.

  2. bool * 지정된 속성

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

    public class Element
    {
        [XmlAttribute("attr")]
        public int Value { get; set; }
    
        [XmlIgnore]
        public bool ValueSpecified;
    }
    
  3. Alex Scordellis 의이 블로그 게시물과 같은 사용자 지정 Nullable

    이 블로그 게시물의 수업을 내 문제에했습니다.

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

    그러나 XmlSerializer가 생성자는 InvalidOperationException과 함께 실패합니다.

    TestConsoleApplication.NullableInt 형식의 'Value'멤버를 직렬화 할 수 없습니다.

    XmlAttribute / XmlText는 IXmlSerializable을 구현하는 형식을 인코딩하는 데 사용할 수 없습니다.}

  4. 추악한 대리 솔루션 (여기 에이 코드를하는 것이 부끄럽습니다. :)) :

    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 인터페이스를 수동으로 구현하는 것이 좋습니다.

현재 전체 Element 클래스에 대해 IXmlSerializable을 구현해야 할 것입니다 (크다). 간단한 해결 방법이 없습니다.


작동합니다.

[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?); }
}

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


나는 늦게되는 생성 화를 엉망으로 만드는 값 유형에 대한 null 데이터를 처리 할 때 다음 기사와 게시물이 도움이되는 것을 발견했습니다.

C #에서 XmlSerializer를 사용하여 값 형식을 nullable로 만드는 방법에 대한 답변 -직렬화 는 XmlSerializer의 멋진 트릭을 자세히 설명합니다. 특히 XmlSerialier는 XXXSpecified 부울 속성을 검색하여 null을 무시할 수있는 포함 여부를 결정합니다.

Alex Scordellis 는 좋은 답변 을 받았습니다. StackOverflow 질문을 받았습니다 . Alex는 또한 자신의 블로그에서 XmlSerializer를 사용하여 Nullable <int>로 역으로 만들었던 문제에 대해 좋은 글을 올렸습니다 .

Xsi : nil 속성 바인딩 지원 에 대한 MSDN 문서 도 유용합니다. IXmlSerializable Interface 에 대한 문서와 마찬가지로 자신의 구현을 작성하는 것이 최후의 수단이어야합니다.


내 대답을 모자에 던질 수 있다고 생각했습니다. IXmlSerializable 인터페이스를 구현하는 사용자 지정 형식을 만들어이 문제를 해결했습니다.

다음 노드가있는 XML 객체가 있다고 가정합니다.

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

그들을 나타내는 개체 :

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
}

변환과 함께 잠재적 인 nullable 항목을 나타내는 동적 nullable 구조체

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

참고 URL : https://stackoverflow.com/questions/1295697/deserializing-empty-xml-attribute-value-into-nullable-int-property-using-xmlseri

반응형