2011-02-14 37 views
12

我有一堆不同的DTO类。它们正在被串行化成一个XML字符串,然后被发送到Web应用程序的客户端。现在,当客户端发回一个XML字符串时,我需要将其反序列化回它所代表的DTO类的一个实例。问题是我想使它成为通用的,并且可能是一个函数,它接受一个xml字符串并吐出一个类型的对象。像这些线很长:xml字符串的通用反序列化

public sometype? Deserialize (string xml) 
{ 
//some code here 
return objectFromXml; 
} 

编辑:可怕的例子!我只是自相矛盾!

我不能做到以下几点:

Person person = Deserialize(personXmlStringFromClient); 

,因为我不知道personXmlStringFromClient是人DTO对象实例的表示。

我不知道什么是序列化对象给我,这似乎是我的问题在这里。我一直在阅读关于反射和其他涉及将类型粘贴到xml中的技术,以便反序列化器知道如何处理它。我似乎无法将它们全部合并为一个工件。另外,在绝大多数例子中,作者知道反序列化后会出现什么类型。任何建议是值得欢迎的!如果我需要在序列化过程中做一些特殊的事情,请分享一下。

回答

20

你可以使用一个通用的:

public T Deserialize<T>(string input) 
     where T : class 
    { 
     System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(T)); 

     using (StringReader sr = new StringReader(input)) 
      return (T)ser.Deserialize(sr); 
    } 

如果你不知道具体的类型将是,我假设你有可能类型的固定号码,你可以尝试反序列化到每一个,直到你不会遇到异常。不是很好,但它会奏效。

或者,您可以检查外部对象名称的xml的开始,并希望能够从那里确定类型。这将取决于xml的外观。

编辑:根据您的编辑,如果主叫方知道他们是路过的类型,可他们提供的完全合格的类型名作为一个字符串作为附加参数的服务?

如果是这样,你可以这样做:

Type t = Type.GetType(typeName); 

,改变Deserialize方法是这样的:

public object Deserialize(string input, Type toType) 
{ 
    System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(toType); 

    using (StringReader sr = new StringReader(input)) 
     return ser.Deserialize(sr); 
} 

然而,这只是让你的object ...如果所有的有问题的类型实现一个通用的接口,你可以像上面那样反序列化,但是将返回类型改为接口(并且在返回语句中将其返回)

+0

对不起!我迷惑了自己。我不能使用你的功能,因为我不知道T是什么。 – Dimskiy 2011-02-14 20:33:14

+0

@Dimskiy:你知道T *可能会是什么吗?(​​也就是潜在类型列表)? – 2011-02-14 20:51:10

+0

如果我可以将字符串作为字符串获取,那么如何将字符串“Person”转换为实际类型? – Dimskiy 2011-02-14 20:53:04

0

abou做一个非通用的“前门”功能,其目的是弄清楚这一点?大多数XML模式使用对象名称或合理的传真作为对象的最外层标签。

0

如果你不介意的仿制药:

public static T DeserializeFromString<T>(string value) 
{ 
    T outObject; 
    XmlSerializer deserializer = new XmlSerializer(typeof(T)); 
    StringReader stringReader = new StringReader(value); 
    outObject = (T)deserializer.Deserialize(stringReader); 
    stringReader.Close(); 
    return outObject; 
} 

编辑:如果你不知道XML会翻译什么类型的对象,以你无法返回除了对象之外任何东西。你当然可以测试你刚刚得到什么样的对象。一种方法可能会通过可能被反序列化的所有类型的对象XmlSerializer

public static object DeserializeFromString(string value, Type[] types) 
{ 
    XmlSerializer deserializer = new XmlSerializer(typeof(object), types); 
    StringReader stringReader = new StringReader(value); 
    object outObject = deserializer.Deserialize(stringReader); 
    stringReader.Close(); 
    return outObject; 
} 

使用此方法,假设你的对象被人挡住(你应该序列方法相同),它给你这样的XML:

<object xsi:type="Person"> 
    ... 
</object> 

(如果你有很多类型的传递,你可以使用反射来得到他们,例如,使用类似Assembly.GetExecutingAssembly().GetTypes()

0

如果有自定义序列化/反序列化程序为每种类型,你可以使用类似这样

public T Deserialize <T>(string xml) 
{ 
    if(typeof(T) == typeof(Person)) 
    { 
     // deserialize and return Person instance 
    } 
    else if(typeof(T) == typeof(Address) 
    { 
     // deserialize and return Address instance 
    } 
    ... 
    ... 
    ... 
} 

你可以叫

Person p = Deserialize<Person>(personXmlStringFromClient); 
1

忘记仿制药。如果你不知道返回类型怎么办?如果您使用的是Visual C#2010或更高版本,则这是新的dynamic关键字的优点。下面是我编写的示例序列化程序类,以及仅使用XML字符串的示例用法,并尝试解析它以获得一般的System类型,成功时返回输出值。你可以扩展它以获得其他更自定义的类型,但它们可能需要有一个默认的构造函数,并且你可能需要做更多的解析以获取类型名称,然后在你的程序集中获取它的路径。它有点棘手,但是一旦你掌握了下面代码的工作方式,它就开始打开一堆可能性。这不正是你想要的吗?你的问题是如何在不知道该类型的情况下获取该类型的对象(但请注意,为了反序列化,您仍然必须在代码中定义该类型)。让我解释。如果您看看assemblyFormatter在下面的代码中的使用方式,您会发现它对您自己定义的类型更为棘手,例如structenum,因为对于这些类型,您必须将assemblyFormatter作为myObject.GetType().FullName。这就是激活器用来调用类型的默认构造函数来创建它以便能够创建序列化器的字符串;它基本上归结为在组装中需要知道类型定义以便能够反序列化它的复杂性。

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Xml.Linq; 
using System.Xml.Serialization; 

namespace DynamicSerializer 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      bool myObject = true; 
      // There are a bunch of other examples you can try out: 
      // string myObject = "Hello, world."; 
      // long myObject = 1000; 
      // int myObject = 100; 
      string mySerializedObject; 
      if (Serializer.TrySerialize(myObject, out mySerializedObject)) 
      { 
       Console.WriteLine("Serialized {0} as {1}.", myObject, mySerializedObject); 
       dynamic myDeserializedObject; 
       if (Serializer.TryDeserialize(mySerializedObject, out myDeserializedObject)) 
       { 
        Console.WriteLine("Deserialized {0} as {1}.", mySerializedObject, myDeserializedObject); 
       } 
      } 
      Console.ReadLine(); 
     } 

     class Serializer 
     { 
      public static bool TrySerialize(dynamic unserializedObject, out string serializedObject) 
      { 
       try 
       { 
        StringWriter writer = new StringWriter(); 
        XmlSerializer serializer = new XmlSerializer(unserializedObject.GetType()); 
        serializer.Serialize(writer, unserializedObject); 
        serializedObject = writer.ToString(); 
        return true; 
       } 
       catch 
       { 
        serializedObject = null; 
        return false; 
       } 
      } 

      // The assemblyFormatter parameter is normally not passed in. However, it may be passed in for cases where the type is a special case (such as for enumerables or structs) that needs to be passed into the serializer. If this is the case, this value should be passed in as yourObject.GetType().FullName. 
      public static bool TryDeserialize(string serializedObject, out dynamic deserializedObjectOut, string assemblyFormatter = "System.{0}") 
      { 
       try 
       { 
        StringReader reader = new StringReader(serializedObject); 
        XDocument document = XDocument.Load(reader); 
        string typeString = null; 
        // Map the object type to the System's default value types. 
        switch (document.Root.Name.LocalName) 
        { 
         case "string": 
          typeString = "String"; 
          break; 
         case "dateTime": 
          typeString = "DateTime"; 
          break; 
         case "int": 
          typeString = "Int32"; 
          break; 
         case "unsignedInt": 
          typeString = "UInt32"; 
          break; 
         case "long": 
          typeString = "Int64"; 
          break; 
         case "unsignedLong": 
          typeString = "UInt64"; 
          break; 
         case "boolean": 
          typeString = "Boolean"; 
          break; 
         case "double": 
          typeString = "Double"; 
          break; 
         case "float": 
          typeString = "Single"; 
          break; 
         case "decimal": 
          typeString = "Decimal"; 
          break; 
         case "char": 
          typeString = "Char"; 
          break; 
         case "short": 
          typeString = "Int16"; 
          break; 
         case "unsignedShort": 
          typeString = "UInt16"; 
          break; 
         case "byte": 
          typeString = "SByte"; 
          break; 
         case "unsignedByte": 
          typeString = "Byte"; 
          break; 
        } 
        if (assemblyFormatter != "System.{0}") 
        { 
         typeString = document.Root.Name.LocalName; 
        } 
        if (typeString == null) 
        { 
         // The dynamic object's type is not supported. 
         deserializedObjectOut = null; 
         return false; 
        } 
        if (typeString == "String") 
        { 
         // System.String does not specify a default constructor. 
         XmlSerializer serializer = new XmlSerializer(typeof(String)); 
         reader = new StringReader(serializedObject); 
         deserializedObjectOut = serializer.Deserialize(reader); 
        } 
        else 
        { 
         object typeReference; 
         if (assemblyFormatter != "System.{0}") 
         { 
          typeReference = Activator.CreateInstance(Type.GetType(assemblyFormatter)); 
         } 
         else 
         { 
          typeReference = Activator.CreateInstance(Type.GetType(String.Format(assemblyFormatter, typeString))); 
         } 
         XmlSerializer serializer = new XmlSerializer(typeReference.GetType()); 
         reader = new StringReader(serializedObject); 
         deserializedObjectOut = serializer.Deserialize(reader); 
        } 
        return true; 
       } 
       catch 
       { 
        deserializedObjectOut = null; 
        return false; 
       } 
      } 
     } 
    } 
}