首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >具有属性值的Xml.Serialization对象T

具有属性值的Xml.Serialization对象T
EN

Stack Overflow用户
提问于 2015-04-03 06:44:35
回答 1查看 179关注 0票数 1

我在模型上使用Xml属性来处理模型的序列化。

基类是:

代码语言:javascript
运行
复制
public class RequestRoot<T>
{
    [XmlElement("Action")]
    public T ActionNode { get; set; }
}

ActionNode类型为T,可以是从字符串到复杂对象集合的任何内容。

示例:

代码语言:javascript
运行
复制
<?xml version="1.0" encoding="UTF-8"?>
<RequestRoot>
<Action Type="TypeValue A">
    <SomeData>Some data</SomeData>
</Action>
</RequestRoot>

<?xml version="1.0" encoding="UTF-8"?>
<RequestRoot>
<Action Type="TypeValue B">
    <MyObjects>
        <MyObject>
            <ObjectValue1>Object Value 1-1</ObjectValue1>
            <ObjectValue2>Object Value 2-1</ObjectValue2>
        </MyObject>
        <MyObject>
            <ObjectValue1>Object Value 1-2</ObjectValue1>
            <ObjectValue2>Object Value 2-2</ObjectValue2>
        </MyObject>
    </MyObjects>
</Action>
</RequestRoot>

我的问题是:是否可以在我的模型上使用Xml属性来编写Type="TypeValue A"Type="TypeValue B",这取决于T是什么?

如果没有,我还有其他选择吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-04-03 17:30:28

没有办法在XmlSerializer开箱即用的情况下做到这一点。这是因为您的RequestRoot类是泛型的,XmlSerializer根据XML和可能的"xsi:type"属性确定要创建的对象的类型。但是,类型信息嵌入到根元素的子元素Action中,在必须分配根元素时无法访问该子元素。

您需要做的是手动读取和编写RequestRoot包装器,然后对内容使用XmlSerializer。例如:

代码语言:javascript
运行
复制
public abstract class RequestRootBase
{
    [XmlIgnore]
    public abstract Type RequestType { get; }

    [XmlIgnore]
    public abstract Object RequestObject { get; }
}

public class RequestRoot<T> : RequestRootBase
{
    public RequestRoot() { }

    public RequestRoot(T ActionNode) { this.ActionNode = ActionNode; }

    [XmlElement("Action")]
    public T ActionNode { get; set; }

    public override Type RequestType
    {
        get { return typeof(T); }
    }

    public override object RequestObject
    {
        get { return ActionNode; }
    }
}

public static class RequestRootHelper
{
    public static RequestRootBase CreateBase(object action)
    {
        if (action == null)
            throw new ArgumentNullException();
        var type = action.GetType();
        return (RequestRootBase)Activator.CreateInstance(typeof(RequestRoot<>).MakeGenericType(type), new [] { action });
    }

    public static RequestRoot<T> Create<T>(T action)
    {
        return new RequestRoot<T> { ActionNode = action };
    }
}

public abstract class RequestRootXmlSerializerBase
{
    const string RequestRootElementName = "RequestRoot";
    const string ActionElementName = "Action";
    const string TypeAttributeName = "Type";

    protected abstract Type BindToType(string name);

    protected abstract string BindToName(Type type);

    static string DefaultRootXmlElementNamespace(Type type)
    {
        var xmlType = type.GetCustomAttribute<XmlRootAttribute>();
        if (xmlType != null && !string.IsNullOrEmpty(xmlType.Namespace))
            return xmlType.Namespace;
        return null;
    }

    public void Serialize(RequestRootBase root, XmlWriter writer)
    {
        writer.WriteStartDocument();
        writer.WriteStartElement(RequestRootElementName);
        writer.WriteStartElement(ActionElementName);
        var typeName = BindToName(root.RequestType);
        writer.WriteAttributeString(TypeAttributeName, typeName);

        var serializer = new XmlSerializer(root.RequestType);

        var rootNameSpace = DefaultRootXmlElementNamespace(root.RequestType);
        var ns = new XmlSerializerNamespaces();
        if (string.IsNullOrEmpty(rootNameSpace))
            ns.Add("", "");
        else
            ns.Add("", rootNameSpace);

        serializer.Serialize(writer, root.RequestObject, ns);

        writer.WriteEndElement();
        writer.WriteEndElement();
        writer.WriteEndDocument();
    }

    public RequestRootBase Deserialize(XmlReader reader)
    {
        if (!reader.ReadToFollowing(RequestRootElementName))
            return null;
        if (!reader.ReadToFollowing(ActionElementName))
            return null;
        var typeName = reader[TypeAttributeName];
        if (typeName == null)
            return null;

        var type = BindToType(typeName);
        if (type == null)
            throw new InvalidDataException();  // THROW AN EXCEPTION in this case

        reader.ReadStartElement();

        var serializer = new XmlSerializer(type);

        var action = serializer.Deserialize(reader);
        if (action == null)
            return null;

        return RequestRootHelper.CreateBase(action);
    }

    public string SerializeToString(RequestRootBase root)
    {
        using (var textWriter = new StringWriter())
        {
            var settings = new XmlWriterSettings() { Indent = true, IndentChars = "    " }; // For cosmetic purposes.
            using (var xmlWriter = XmlWriter.Create(textWriter, settings))
                Serialize(root, xmlWriter);
            return textWriter.ToString();
        }
    }

    public RequestRootBase DeserializeFromString(string xml)
    {
        using (var sr = new StringReader(xml))
        using (var xmlReader = XmlReader.Create(sr))
        {
            return Deserialize(xmlReader);
        }
    }
}

public class RequestRootXmlSerializer : RequestRootXmlSerializerBase
{
    readonly Dictionary<string, Type> nameToType = new Dictionary<string, Type>();
    readonly Dictionary<Type, string> typeToName = new Dictionary<Type, string>();

    const string ListPrefix = "ArrayOf";
    const string ListPostFix = "";

    protected override string BindToName(Type type)
    {
        return typeToName[type];
    }

    protected override Type BindToType(string name)
    {
        return nameToType[name];
    }

    public RequestRootXmlSerializer(IEnumerable<Type> types)
    {
        if (types == null)
            throw new ArgumentNullException();
        foreach (var type in types)
        {
            if (type.IsInterface || type.IsAbstract)
                throw new ArgumentException();
            var name = DefaultXmlElementName(type);
            nameToType.Add(name, type);
            typeToName.Add(type, name);
        }
    }

    static string DefaultXmlElementName(Type type)
    {
        if (type.IsGenericType
            && type.GetGenericTypeDefinition() == typeof(List<>))
        {
            var elementType = type.GetGenericArguments()[0];
            return ListPrefix + DefaultXmlElementName(elementType) + ListPostFix;
        }
        else
        {
            var xmlRoot = type.GetCustomAttribute<XmlRootAttribute>();
            if (xmlRoot != null && !string.IsNullOrEmpty(xmlRoot.ElementName))
                return xmlRoot.ElementName;
            var xmlType = type.GetCustomAttribute<XmlTypeAttribute>();
            if (xmlType != null && !string.IsNullOrEmpty(xmlType.TypeName))
                return xmlType.TypeName;
            return type.Name;
        }
    }
}

您可能希望用您自己的类型到名称映射方案来取代我的映射方案;它只是一个原型。

然后像这样使用它:

代码语言:javascript
运行
复制
[XmlRoot("A", Namespace="ATestNameSpace")]
public class ClassA
{
    [XmlText]
    public string Value { get; set; }
}

public class MyObject
{
    public string ObjectValue1 { get; set; }
    public string ObjectValue2 { get; set; }
}

public class TestClass
{
    public static void Test()
    {
        var root1 = RequestRootHelper.Create(new ClassA { Value = "Some data" });
        var root2 = RequestRootHelper.Create(new List<MyObject> { new MyObject { ObjectValue1 = "Object Value 1-1", ObjectValue2 = "Object Value 2-1" }, new MyObject { ObjectValue1 = "Object Value 1-2", ObjectValue2 = "Object Value 2-2" } });

        var serializer = new RequestRootXmlSerializer(new[] { typeof(ClassA), typeof(List<ClassA>), typeof(MyObject), typeof(List<MyObject>) });

        TestRootSerialization(root1, serializer);

        TestRootSerialization(root2, serializer);
    }

    private static void TestRootSerialization<T>(RequestRoot<T> root, RequestRootXmlSerializer serializer)
    {
        var xml1 = serializer.SerializeToString(root);
        Debug.WriteLine(xml1);
        var root11 = serializer.DeserializeFromString(xml1);
        Debug.Assert(root.GetType() == root11.GetType()); // NO ASSERT
        var xml11 = serializer.SerializeToString(root11);
        Debug.WriteLine(xml11);
        Debug.Assert(xml1 == xml11); // NO ASSERT
    }
}

这将为ClassA生成以下XML输出

代码语言:javascript
运行
复制
<RequestRoot>
    <Action Type="A">
        <A xmlns="ATestNameSpace">Some data</A>
    </Action>
</RequestRoot>

对于List<MyObject>

代码语言:javascript
运行
复制
<RequestRoot>
    <Action Type="ArrayOfMyObject">
        <ArrayOfMyObject>
            <MyObject>
                <ObjectValue1>Object Value 1-1</ObjectValue1>
                <ObjectValue2>Object Value 2-1</ObjectValue2>
            </MyObject>
            <MyObject>
                <ObjectValue1>Object Value 1-2</ObjectValue1>
                <ObjectValue2>Object Value 2-2</ObjectValue2>
            </MyObject>
        </ArrayOfMyObject>
    </Action>
</RequestRoot>
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29427475

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档