81

サードパーティからxmlを取得し、それをC#オブジェクトに逆シリアル化する必要があります。このxmlには、整数型または空の値の属性が含まれている場合があります:attr =” 11”またはattr =””。この属性値をnull許容整数型のプロパティに逆シリアル化したい。ただし、XmlSerializerはnull許容型への逆シリアル化をサポートしていません。次のテストコードは、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で失敗します。

XMLドキュメント(1、16)にエラーがあります。

空の値を持つ属性をnull許容型に(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. AlexScordellisによるこのブログ投稿のようなカスタム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を実装する必要があるようです(これは大きいです)。簡単な回避策はありません…</ p>

4

5 に答える 5

69

これは機能するはずです:

[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?); }
}
于 2009-09-25T19:36:51.693 に答える
23

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);
    }
}
于 2009-08-20T09:18:27.107 に答える
13

私は最近、シリアル化を何度もいじっていますが、値型のnullデータを処理するときに、次の記事や投稿が役立つことがわかりました。

C#のXmlSerializerで値型をnull許容にする方法への答え-シリアル化は、XmlSerializerのかなり気の利いたトリックを詳しく説明しています。具体的には、XmlSerialierは、XXXSpecifiedブール値プロパティを検索して、nullを無視できるようにする必要があるかどうかを判断します。

Alex ScordellisがStackOverflowの質問をしましたが、良い答えが返ってきました。Alexはまた、彼が解決しようとしていた問題について、 XmlSerializerを使用してNullable<int>に逆シリアル化することについてブログに良い記事を書きました。

Xsi:nil属性バインディングサポートに関するMSDNドキュメントも役立ちます。IXmlSerializableインターフェイスのドキュメントと同様に、独自の実装を作成することが最後の手段になるはずです。

于 2009-08-18T18:57:22.920 に答える
2

私は自分の答えを帽子に投げ込むほうがよいと思いました: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
}

変換とともに潜在的なnull許容エントリを表す動的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));
        }
    }
}
于 2017-01-16T23:12:51.657 に答える
2

にをロードしxmlXmlDocument次にこれを逆シリアル化して、探しているJsonオブジェクトを取得することによって、これを行うこともできます。T

        public static T XmlToModel<T>(string xml)
        {

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xml);

            string jsonText = JsonConvert.SerializeXmlNode(doc);

            T result = JsonConvert.DeserializeObject<T>(jsonText);

            return result;
        }

于 2020-09-30T09:28:05.637 に答える