9

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

クラスをシリアライズしようとすると、インターフェイスをシリアライズできないと表示されます。シリアライザーは、インターフェイスを実装していることだけを知っており、プロパティのクラスの構造に関する知識がないことを理解しています。

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

編集:私の意図を明確にする:私がこのクラスを持っているとしましょう:

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

クラスユーザーにはクラスがあります

Car myCar = new Car();
myCar.Engine = new ElectricEngine() {batteryPrecentageLeft= 70};

クラス myCar をシリアライズすると、xml は次のようになると思います。

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

4 に答える 4

6

プロパティを除外対象としてマークできます。

ただし、より深刻な問題があります。シリアライゼーションは単純な「状態」しかキャプチャできず、動作はキャプチャできません。あなたのクラスは直列化可能な種類ではありません。逆シリアル化後にプロパティが持つと予想される「値」は何ですか? nullが唯一のオプションです。

適切な回避策は、実際に保存する必要があるものを考え、その部分に DTO を使用することです。


次のモデルはシリアル化できます。

public class BaseEngine { }

[XmlInclude(typeof(InternalCombustionEngine))]
[XmlInclude(typeof(ElectricEngine))]
public class Car
{      
    public BaseEngine Engine { get; set; }
}
于 2012-04-20T19:24:45.010 に答える
5

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

アップデート

基本クラスを使用することは、実際には選択肢ではないことに気づきました。

おそらく最善の解決策は、HenkHoltermanが述べたようなDTOで回避策を実行することです。

しかし、本当に質問の解決策が必要な場合は、独自のカスタムシリアライザーを作成する必要があると思いますが、解決するために多くのバグが発生するため、お勧めしません。

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

これが単なる例以上のものとして機能するためには、少なくとも2つのことを追加する必要があります。

  1. 例外処理
  2. xml要素の値をキャストまたは変換して、行のタイプを修正しますanyThingProperty.SetValue(obj, propertyElement.Value, null);
[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
{
}
于 2012-04-20T19:23:58.980 に答える
3

@Jens ソリューションに基づいて、必要なことを行うシリアライザーを作成しました。ありがとうジェン。コードは次のとおりです。

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;
    }
}
于 2012-04-23T16:08:40.133 に答える