2012-04-20 17 views
8

インターフェイスとして定義されたプロパティを持つクラスがあります。 私のクラスのユーザは、このプロパティを実装するクラス実装をこのプロパティに割り当てることができます。 ディスク上のテキストファイルからこのクラス状態をロードできるようにします。ユーザーは、アプリケーションの操作を制御するために、xmlファイルを手動で変更できる必要があります。インターフェイス型メンバーをシリアル化する方法

クラスをシリアル化しようとすると、インターフェイスをシリアル化できないことがわかります。 シリアライザはプロパティのクラスの構造について知識がなく、インターフェイスを実装していることを知っています。

メンバーのGetTypeを呼び出して、実際のクラスの構造に反映されると思います。これを達成する方法はありますか? 要件を実装する別の方法はありますか?

編集:私の意図を明確化: が、私はこのクラスを考えてみましょう:

class Car 
{ 
IEngine engine 
} 
class ElectricEngine : IEngine 
{ 
int batteryPrecentageLeft; 
} 
class InternalCombustionEngine : IEngine 
{ 
int gasLitersLeft; 
} 

と私はクラスてmyCarをシリアル化するとき、クラスのユーザーが

を持つクラスを持って、I xmlが次のようになることを期待してください。

<Car> 
<Engine> 
<ElectricEngine> 
<batteryPrecentageLeft>70</batteryPrecentageLeft> 
</ElectricEngine> 
<Engine> 
</Car> 

答えて

2

私は何が必要ないシリアライザを作成している@Jensソリューションに基づいています。ありがとうJen。ここ はコードです:

public class RuntimeXmlSerializerAttribute : XmlIgnoreAttribute { } 

public class RuntimeXmlSerializer 
{ 
    private Type m_type; 
    private XmlSerializer m_regularXmlSerializer; 

    private const string k_FullClassNameAttributeName = "FullAssemblyQualifiedTypeName"; 

    public RuntimeXmlSerializer(Type i_subjectType) 
    { 
     this.m_type = i_subjectType; 
     this.m_regularXmlSerializer = new XmlSerializer(this.m_type); 
    } 

    public void Serialize(object i_objectToSerialize, Stream i_streamToSerializeTo) 
    { 
     StringWriter sw = new StringWriter(); 
     this.m_regularXmlSerializer.Serialize(sw, i_objectToSerialize); 
     XDocument objectXml = XDocument.Parse(sw.ToString()); 
     sw.Dispose(); 
     SerializeExtra(i_objectToSerialize,objectXml); 
     string res = objectXml.ToString(); 
     byte[] bytesToWrite = Encoding.UTF8.GetBytes(res); 
     i_streamToSerializeTo.Write(bytesToWrite, 0, bytesToWrite.Length); 
    } 

    public object Deserialize(Stream i_streamToSerializeFrom) 
    { 
     string xmlContents = new StreamReader(i_streamToSerializeFrom).ReadToEnd(); 
     StringReader sr; 
     sr = new StringReader(xmlContents); 
     object res = this.m_regularXmlSerializer.Deserialize(sr); 
     sr.Dispose(); 
     sr = new StringReader(xmlContents); 
     XDocument doc = XDocument.Load(sr); 
     sr.Dispose(); 
     deserializeExtra(res, doc); 
     return res; 
    } 

    private void deserializeExtra(object i_desirializedObject, XDocument i_xmlToDeserializeFrom) 
    { 
     IEnumerable propertiesToDeserialize = i_desirializedObject.GetType() 
      .GetProperties().Where(p => p.GetCustomAttributes(true) 
       .FirstOrDefault(a => a.GetType() == 
        typeof(RuntimeXmlSerializerAttribute)) != null); 
     foreach (PropertyInfo prop in propertiesToDeserialize) 
     { 
      XElement propertyXml = i_xmlToDeserializeFrom.Descendants().FirstOrDefault(e => 
       e.Name == prop.Name); 
      if (propertyXml == null) continue; 
      XElement propertyValueXml = propertyXml.Descendants().FirstOrDefault(); 
      Type type = Type.GetType(propertyValueXml.Attribute(k_FullClassNameAttributeName).Value.ToString()); 
      XmlSerializer srl = new XmlSerializer(type); 
      object deserializedObject = srl.Deserialize(propertyValueXml.CreateReader()); 
      prop.SetValue(i_desirializedObject, deserializedObject, null); 
     } 
    } 

    private void SerializeExtra(object objectToSerialize, XDocument xmlToSerializeTo) 
    { 
     IEnumerable propertiesToSerialize = 
      objectToSerialize.GetType().GetProperties().Where(p => 
       p.GetCustomAttributes(true).FirstOrDefault(a => 
        a.GetType() == typeof(RuntimeXmlSerializerAttribute)) != null); 
     foreach (PropertyInfo prop in propertiesToSerialize) 
     { 
      XElement serializedProperty = new XElement(prop.Name); 
      serializedProperty.AddFirst(serializeObjectAtRuntime(prop.GetValue(objectToSerialize, null))); 
      xmlToSerializeTo.Descendants().First().Add(serializedProperty); //TODO 
     } 
    } 

    private XElement serializeObjectAtRuntime(object i_objectToSerialize) 
    { 
     Type t = i_objectToSerialize.GetType(); 
     XmlSerializer srl = new XmlSerializer(t); 
     StringWriter sw = new StringWriter(); 
     srl.Serialize(sw, i_objectToSerialize); 
     XElement res = XElement.Parse(sw.ToString()); 
     sw.Dispose(); 
     XAttribute fullClassNameAttribute = new XAttribute(k_FullClassNameAttributeName, t.AssemblyQualifiedName); 
     res.Add(fullClassNameAttribute); 

     return res; 
    } 
} 
5

多分、インターフェイスの代わりに基本クラスを使用して、それをシリアル化することができます。

更新

私は、基本クラスを使用すると、本当にあなたのためのオプションではありませんでしたことに気づきました。

おそらく最も良い解決策は、Henk HoltermanのようにDTOで回避策を講じることです。

しかし、実際にあなたの質問に対する解決策が必要なら、私はあなた自身のカスタムシリアライザを作成しなければならないと思いますが、ソートアウトには多くのバグが残るのでお勧めしません。

ここではカスタムシリアライザの例を示しますが、この例では は実際のアプリケーションで完全に使用する必要があることに注意してください。

少なくとも二つのこと、これは単なる例以上のために働くために添加されなければならない。

  1. 例外
  2. 鋳造を処理または線anyThingProperty.SetValue(obj, propertyElement.Value, null);
  3. 上のタイプを修正するためにXML要素値を変換します
[TestClass] 
public class SerializableInterfaceTest 
{ 
    [TestMethod] 
    public void TestMethod1() 
    { 
     string serialize = AnyThingSerializer.Serialize(
      new SerializableClass {Name = "test", Description = "test1", 
       AnyThing = new Animal {Name = "test", Color = "test1"}}); 
     Console.WriteLine(serialize); 
     object obj = AnyThingSerializer.Deserialize(serialize); 
    } 
} 

public sealed class SerializableClass 
{ 
    public string Name { get; set; } 
    public string Description { get; set; } 

    [AnyThingSerializer] 
    public object AnyThing { get; set; } 
} 

public static class AnyThingSerializer 
{ 
    public static string Serialize(object obj) 
    { 
     Type type = obj.GetType(); 
     var stringBuilder = new StringBuilder(); 
     var serializer = new XmlSerializer(type); 
     serializer.Serialize(new StringWriter(stringBuilder), obj); 
     XDocument doc = XDocument.Load(new StringReader(stringBuilder.ToString())); 
     foreach (XElement xElement in SerializeAnyThing(obj)) 
     { 
      doc.Descendants().First().Add(xElement); 
     } 
     return doc.ToString(); 
    } 

    public static object Deserialize(string xml) 
    { 
     var serializer = new XmlSerializer(typeof (T)); 
     object obj = serializer.Deserialize(new StringReader(xml)); 
     XDocument doc = XDocument.Load(new StringReader(xml)); 
     DeserializeAnyThing(obj, doc.Descendants().OfType().First()); 
     return obj; 
    } 

    private static void DeserializeAnyThing(object obj, XElement element) 
    { 
     IEnumerable anyThingProperties = obj.GetType() 
      .GetProperties().Where(p => p.GetCustomAttributes(true) 
       .FirstOrDefault(a => a.GetType() == 
        typeof (AnyThingSerializerAttribute)) != null); 
     foreach (PropertyInfo anyThingProperty in anyThingProperties) 
     { 
      XElement propertyElement = element.Descendants().FirstOrDefault(e => 
       e.Name == anyThingProperty.Name && e.Attribute("type") != null); 
      if (propertyElement == null) continue; 
      Type type = Type.GetType(propertyElement.Attribute("type").Value); 
      if (IsSimpleType(type)) 
      { 
       anyThingProperty.SetValue(obj, propertyElement.Value, null); 
      } 
      else 
      { 
       object childObject = Activator.CreateInstance(type); 
       DeserializeAnyThing(childObject, propertyElement); 
       anyThingProperty.SetValue(obj, childObject, null); 
      } 
     } 
    } 

    private static List SerializeAnyThing(object obj) 
    { 
     var doc = new List(); 
     IEnumerable anyThingProperties = 
      obj.GetType().GetProperties().Where(p => 
       p.GetCustomAttributes(true).FirstOrDefault(a => 
        a.GetType() == typeof (AnyThingSerializerAttribute)) != null); 
     foreach (PropertyInfo anyThingProperty in anyThingProperties) 
     { 
      doc.Add(CreateXml(anyThingProperty.Name, 
       anyThingProperty.GetValue(obj, null))); 
     } 
     return doc; 
    } 

    private static XElement CreateXml(string name, object obj) 
    { 
     var xElement = new XElement(name); 
     Type type = obj.GetType(); 
     xElement.Add(new XAttribute("type", type.AssemblyQualifiedName)); 
     foreach (PropertyInfo propertyInfo in type.GetProperties()) 
     { 
      object value = propertyInfo.GetValue(obj, null); 
      if (IsSimpleType(propertyInfo.PropertyType)) 
      { 
       xElement.Add(new XElement(propertyInfo.Name, value.ToString())); 
      } 
      else 
      { 
       xElement.Add(CreateXml(propertyInfo.Name, value)); 
      } 
     } 
     return xElement; 
    } 

    private static bool IsSimpleType(Type type) 
    { 
     return type.IsPrimitive || type == typeof (string); 
    } 
} 

public class AnyThingSerializerAttribute : XmlIgnoreAttribute 
{ 
} 
+0

です。 –

+0

基本的には、インターフェイスがより適切に見えるので、基本クラスの使用を避けたかったのです。また、ユーザーがどのような具体的な種類のものを使用するかも事前にはわかりません。 (私はXmlIncludeでそれらを宣言する必要があることを理解しています)。ユーザーが使用する可能性のあるクラスの種類を事前に知っていることを避けることはできますか? – itaysk

+0

@Henk、OK私は今質問を理解していると思います。はい、XmlIncludeを使用することを意味します。これは私の提案を無益なものにするでしょう。 –

7

プロパティをdo-not-includeとしてマークすることができます。

しかし、より深刻な問題があります。シリアライゼーションでは、動作ではなく単純な「状態」しか取得できません。あなたのクラスはシリアライズ可能な種類ではありません。 deserialization後にプロパティが持つ価値は何ですか? nullは唯一のオプションです。

適切な回避策は、実際には何を保存し、その部分にDTOを使用するかを考えることです。


次のモデルをシリアライズすることができます。

public class BaseEngine { } 

[XmlInclude(typeof(InternalCombustionEngine))] 
[XmlInclude(typeof(ElectricEngine))] 
public class Car 
{  
    public BaseEngine Engine { get; set; } 
} 
+0

私はそれをシリアル化する必要があるので、do not-includeとしてマークしたくないです:)。あなたは "_直列化の前にあった値と同じ値を、逆直列化後にプロパティが持つと期待していますか?"と尋ねます。 (私の質問で説明した) – itaysk

+1

"シリアライゼーション前と同じ値" - 実装インスタンスもシリアライズされていますか? –

+0

@HenkHolterman Windows 8 IE 10 ...事故が発生しました。申し訳ありません、元に戻すことを試みています。投稿を編集すると、私はそれを変更することができます....ごめんなさい。 –

1

あなたはExtendedXmlSerializerを使用することができます。 あなたはクラスがある場合:

public interface IEngine 
{ 
    string Name {get;set;} 
} 

public class Car 
{ 
    public IEngine Engine {get;set;} 
} 


public class ElectricEngine : IEngine 
{ 
    public string Name {get;set;} 
    public int batteryPrecentageLeft {get;set;} 
} 

public class InternalCombustionEngine : IEngine 
{ 
    public string Name {get;set;} 
    public int gasLitersLeft {get;set;} 
} 

と、このクラスのインスタンスを作成します。

Car myCar = new Car(); 
myCar.Engine = new ElectricEngine() {batteryPrecentageLeft= 70, Name = "turbo diesel"}; 

をあなたはExtendedXmlSerializerを使用して、このオブジェクトをシリアル化することができます

ExtendedXmlSerializer serializer = new ExtendedXmlSerializer(); 
var xml = serializer.Serialize(myCar); 

出力XMLは次のようになります

<?xml version="1.0" encoding="utf-8"?> 
<Car type="Program+Car"> 
    <Engine type="Program+ElectricEngine"> 
     <Name>turbo diesel</Name> 
     <batteryPrecentageLeft>70</batteryPrecentageLeft> 
    </Engine> 
</Car> 

あなたがnugetからExtendedXmlSerializerをインストールするか、次のコマンドを実行できます。

Install-Package ExtendedXmlSerializer 

ここでは、これは `タイプを発表する` [XmlInclude()]で、動作しますonline example

関連する問題