xml字符串的通用反序列化

时间:2011-02-14 20:25:59

标签: .net xml serialization c#-4.0 xml-serialization

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

public sometype? Deserialize (string xml)
{
//some code here
return objectFromXml;
}
编辑:可怕的例子!我只是自相矛盾!

我无法做到以下几点:

Person person = Deserialize(personXmlStringFromClient);

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

我不知道给了我什么序列化对象,这似乎是我的问题。我一直在阅读有关反射和其他技术的内容,这些技术涉及将类型粘贴到xml中,以便反序列化器知道如何处理它。我似乎无法将它们整合成一个工件。此外,在大多数示例中,作者知道反序列化后将会有什么类型。欢迎任何建议!如果我需要对序列化过程做一些特别的事情,请分享一下。

5 个答案:

答案 0 :(得分:22)

您可以使用通用:

    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的外观。

编辑:根据您的编辑,如果调用者知道他们传递的类型,他们是否可以提供完全限定的typename作为字符串作为服务的附加参数?

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

    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 ...如果所讨论的所有类型都实现了一个公共接口,你可以像上面那样反序列化,但是将返回类型更改为接口(并在其中强制转换为接口)退货声明)

答案 1 :(得分:1)

如果你不介意泛型:

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()的内容)

答案 2 :(得分: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;
                }
            }
        }
    }
}

答案 3 :(得分:0)

如何制作一个非通用的“前门”功能,其目的是为了解决这个问题?大多数XML模式使用对象名称或合理的传真作为对象的最外层标记。

答案 4 :(得分: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);