2012-02-21 11 views
12

いくつかのプロパティを持つオブジェクトをシリアル化しようとしていますが、シリアル化にすべてのプロパティを含めたくありません。また、日付形式を変更したいと思います。元のクラスを変更せずにシリアライズ中にいくつかのプロパティを除外する

もちろん、[XmlIgnore]を追加できますが、元のクラスを変更することはできません。

私が考えることができる唯一の選択肢は、新しいクラスを作成し、2つのクラス間ですべてのコンテンツをコピーすることでした。しかし、それは醜いかもしれませんし、多くの手動コードが必要になります。

オリジナルは抽象ではないので、おそらくサブクラスを作成することは可能でしょうか?

私の質問は、このようです:

  1. 私は元のクラスを変更することなく、いくつかのプロパティを除外することができますどのように?

  2. 出力XMLの日付形式をカスタマイズするにはどうすればよいですか?

要件:可能

  • として

    1. として強く型付けされた

      直列化されたXMLは、事前に

    おかげdeserializableでなければなりません。

  • +0

    ところで、私たちは.NET 4.0 – Schiavini

    +0

    を使用していますそしてXmlSerializer – Schiavini

    答えて

    11

    を実装しようとすることができ、私はXmlAttributeOverridesを使用することにしましたが、それらより強力な型指定された(私は文字列としてプロパティ名を入力するために嫌い)製。ここで私はそれのために使用される拡張メソッドです:

    public static void Add<T>(this XmlAttributeOverrides overrides, Expression<Func<T, dynamic>> propertySelector, XmlAttributes attributes) 
        { 
         overrides.Add(typeof(T), propertySelector.BuildString(), attributes); 
        } 
    
        public static string BuildString(this Expression propertySelector) 
        { 
         switch (propertySelector.NodeType) 
         { 
          case ExpressionType.Lambda: 
           LambdaExpression lambdaExpression = (LambdaExpression)propertySelector; 
           return BuildString(lambdaExpression.Body); 
    
          case ExpressionType.Convert: 
          case ExpressionType.Quote: 
           UnaryExpression unaryExpression = (UnaryExpression)propertySelector; 
           return BuildString(unaryExpression.Operand); 
    
          case ExpressionType.MemberAccess: 
    
           MemberExpression memberExpression = (MemberExpression)propertySelector; 
           MemberInfo propertyInfo = memberExpression.Member; 
    
           if (memberExpression.Expression is ParameterExpression) 
           { 
            return propertyInfo.Name; 
           } 
           else 
           { 
            // we've got a nested property (e.g. MyType.SomeProperty.SomeNestedProperty) 
            return BuildString(memberExpression.Expression) + "." + propertyInfo.Name; 
           } 
    
          default: 
           // drop out and throw 
           break; 
         } 
         throw new InvalidOperationException("Expression must be a member expression: " + propertySelector.ToString()); 
        } 
    

    を次に、属性を無視することは、私は美しく無視リストに追加することができます:

    var overrides = new XmlAttributeOverrides(); 
        var ignore = new XmlAttributes { XmlIgnore = true }; 
        overrides.Add<MyClass>(m => m.Id, ignore); 
        overrides.Add<MyClass>(m => m.DateChanged, ignore); 
        Type t = typeof(List<MyClass>); 
        XmlSerializer serial = new XmlSerializer(t, overrides); 
    
    +3

    'XmlAttributeOverrides'を使って構築された' XmlSerializer'は、一度構築してから後で再利用できるようにキャッシュする必要があります。説明については、[StreamReaderとXmlSerializerを使用したメモリリーク](https://stackoverflow.com/questions/23897145)と[XmlSerializer extraTypes memory leak](https://stackoverflow.com/questions/38892352)を参照してください。 – dbc

    3

    XmlSerializerを使用している場合は、おそらくXmlAttributeOverridesが必要です。

    更新日: 私は日付形式をカスタマイズする可能性を調査していますが、私が見る限り、解決策はありません。

    他の人に言われているように、1つのオプションはIXmlSerializableを実装することです。これは、オブジェクト全体(-graph)を完全に(非)直列化する責任があるという欠点があります。

    2つ目のオプションは、非常に大きな欠点を持つリストで、基本クラスをサブクラス化することです(これはあなたの投稿の代わりに使用しました)。それはきれいではありませんが、それは動作します

    public class Test 
    { 
        public int Prop { get; set; } 
        public DateTime TheDate { get; set; } 
    } 
    
    public class SubTest : Test 
    { 
        private string _customizedDate; 
        public string CustomizedDate 
        { 
         get { return TheDate.ToString("yyyyMMdd"); } 
         set 
         { 
          _customizedDate = value; 
          TheDate = DateTime.ParseExact(_customizedDate, "yyyyMMdd", null); 
         } 
        } 
    
        public Test Convert() 
        { 
         return new Test() { Prop = this.Prop }; 
        } 
    } 
    
    // Serialize 
    XmlAttributeOverrides overrides = new XmlAttributeOverrides(); 
    XmlAttributes attributes = new XmlAttributes(); 
    attributes.XmlIgnore = true; 
    overrides.Add(typeof(Test), "TheDate", attributes); 
    
    XmlSerializer xs = new XmlSerializer(typeof(SubTest), overrides); 
    SubTest t = new SubTest() { Prop = 10, TheDate = DateTime.Now, CustomizedDate="20120221" }; 
    xs.Serialize(fs, t); 
    
    // Deserialize 
    XmlSerializer xs = new XmlSerializer(typeof(SubTest)); 
    SubTest t = (SubTest)xs.Deserialize(fs); 
    Test test = t.Convert(); 
    

    :かなりいくつかの配管、元のオブジェクトからとへの変換、およびXmlAttributeOverridesを使用すると、あなたはこのような何かを構築することができます。

    この場合、SubTestオブジェクトを実際に(非)直列化していることに注意してください。正確なタイプが重要な場合は、これもオプションではありません。

    7

    XmlSerializerが出力にNULLをシリアル化しないという事実を利用して、いくつかのプロパティを除外することができます。したがって、参照型の場合は、xmlに表示したくないプロパティをnullにすることができます。

    結果のXMLは、同じクラスに逆シリアル化されますが、省略されたフィールドは明らかにnullになります。

    ただし、これは日付書式を変更したい場合には役に立ちません。このためには、日付を文字列として新しいクラスを作成するか、IXmlSerializableを実装してxmlを完全に制御する必要があります。 [dateデータ型がXMLで標準フォーマットを持っていることに注意する価値があるので、それを変更することで、それは厳密にXML日付ではなくなるでしょう - あなたは気にしないかもしれません。

    [あなたのコメントに応答して、EDIT]

    ありますがnull NULL可能タイプを「消える」するために使用する可能性のある追加のトリックがあるが、それはあなたのクラスへの変更を必要としません。 シリアライザは、MyPropertyをシリアル化するときに、MyProperySpecifiedというプロパティがあるかどうかもチェックします。それが存在し、falseを返した場合、アイテムのプロパティがシリアライズされていません:あなたは、このプロパティを追加するために用意されている場合

    public class Person 
    { 
        [XmlElement] 
        public string Name { get; set; } 
    
        [XmlElement] 
        public DateTime? BirthDate { get; set; } 
    
        public bool BirthDateSpecified 
        { 
         get { return BirthDate.HasValue; } 
        } 
    } 
    

    あなたがするときはnull、それがNULL可能なタイプを削除することができます。実際には - これについて考える - これは、使用シナリオに応じて他のプロパティも削除する便利な方法です。

    +0

    私のプロパティがnullの場合、それらは Schiavini

    +1

    というタグを取得します。それらを 'IsNullable'としてマークしましたか?そうなら、 :nil = "true" 'ではなく、省略します。 –

    +0

    それらのうちのいくつかは 'DateTime?'か 'int?'ですが、文字列でもあります。 – Schiavini

    2

    最初のオプションは、XmlAttributeOverridesクラスを使用することです。

    それとも、派生クラスを作成し、興味を持っている誰のためにIXmlSerializable interface

    public class Program 
    { 
        static void Main(string[] args) 
        { 
         StringWriter sr1 = new StringWriter(); 
         var baseSerializer = new XmlSerializer(typeof(Human)); 
         var human = new Human {Age = 30, Continent = Continent.America}; 
         baseSerializer.Serialize(sr1, human); 
         Console.WriteLine(sr1.ToString()); 
         Console.WriteLine(); 
    
         StringWriter sr2 = new StringWriter(); 
         var specialSerializer = new XmlSerializer(typeof(SpecialHuman)); 
         var special = new SpecialHuman() {Age = 40, Continent = Continent.Africa}; 
         specialSerializer.Serialize(sr2, special); 
         Console.WriteLine(sr2.ToString()); 
         Console.ReadLine(); 
        } 
    
        public enum Continent 
        { 
         Europe, 
         America, 
         Africa 
        } 
    
        public class Human 
        { 
         public int Age { get; set; } 
         public Continent Continent { get; set; } 
        } 
    
        [XmlRoot("Human")] 
        public class SpecialHuman : Human, IXmlSerializable 
        { 
         #region Implementation of IXmlSerializable 
    
         /// <summary> 
         /// This method is reserved and should not be used. When implementing the IXmlSerializable interface, you should return null (Nothing in Visual Basic) from this method, and instead, if specifying a custom schema is required, apply the <see cref="T:System.Xml.Serialization.XmlSchemaProviderAttribute"/> to the class. 
         /// </summary> 
         /// <returns> 
         /// An <see cref="T:System.Xml.Schema.XmlSchema"/> that describes the XML representation of the object that is produced by the <see cref="M:System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)"/> method and consumed by the <see cref="M:System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)"/> method. 
         /// </returns> 
         public XmlSchema GetSchema() 
         { 
          throw new NotImplementedException(); 
         } 
    
         public void ReadXml(XmlReader reader) 
         { 
          throw new NotImplementedException(); 
         } 
    
         public void WriteXml(XmlWriter writer) 
         { 
          writer.WriteElementString("Age", Age.ToString()); 
          switch(Continent) 
          { 
           case Continent.Europe: 
           case Continent.America: 
            writer.WriteElementString("Continent", this.Continent.ToString()); 
            break; 
           case Continent.Africa: 
            break; 
           default: 
            throw new ArgumentOutOfRangeException(); 
          } 
         } 
    
         #endregion 
        } 
    
    } 
    
    +0

    これはうまく見えますが、多くのプロパティを使用すると、多くの手作業でのコーディングが必要になります。 – Schiavini

    関連する問題