C# 如何从XML反序列化未知类型?

C# 如何从XML反序列化未知类型?,c#,xml,deserialization,C#,Xml,Deserialization,考虑以下过度简化的XML块: <ElementA> <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#integer">5 </AttributeValue> </ElementA> 具体地说,从DataType属性看AttributeValue元素,我知道我的值是integer类型的,尽管它可能是双精度、字符串、日期时间……w3标准中任何已建立的数据类型。我想将此x

考虑以下过度简化的XML块:

<ElementA>
   <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#integer">5
   </AttributeValue>
</ElementA>
具体地说,从DataType属性看AttributeValue元素,我知道我的值是integer类型的,尽管它可能是双精度、字符串、日期时间……w3标准中任何已建立的数据类型。我想将此xml反序列化为具有强类型值的.NET类。我想到的第一件事是创建一个通用AttributeValue类:

public class AttributeValue<T>
{
    public T Value {get; set;}
}
但当然,这不起作用有两个原因-最大的一个原因是我必须在父类中声明类型,因为没有定义t,所以它不会编译:

public class ElementA
{
    public AttributeValue<T> {get; set; }  // Naturally, this will not work because T
}                                          // is not defined.
另外,我可能必须在类上实现IXmlSerializable来处理自定义序列化

有没有更好的办法来解决这个问题?我知道我可以序列化代码中的DataType属性,并将值存储为字符串,然后在以后转换,但在业务对象中实际拥有正确的类型以供以后处理会很有帮助

谢谢你的帮助


Jason

我知道这不是对您问题的确切答案,但您可以在.NET4中使用dynamics实现一个解决方案。以下是一个例子:

public class DynamicElement : DynamicObject
{
    public Dictionary<string, object> Attributes
    {
        get { return lst; }
    }

    private Dictionary<string, object> lst;

    public DynamicElement()
    {
        lst = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase);
    }

    public bool Present(string name)
    {
        if (lst == null) return false;
        if (!lst.ContainsKey(name)) return false;

        return true;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        var name = binder.Name;
        result = null;

        if (lst == null) return false;
        if (!lst.ContainsKey(name)) return false;

        result = lst[name];
        return true;
    }
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        var name = binder.Name;

        if (lst == null) return false;

        if (!lst.ContainsKey(name))
            lst.Add(name, value);
        else
            lst[name] = value;

        return true;
    }
}
后来:

public void something(DynamicElement de)
{
    dynamic d = de;
    if(d.Done) //remember, defined this above.. just an example.
    {
        int someValue = d.AttributeValue;
    }
}
缺点是没有智能感知。这一切都在运行时解决。您还可以检查d.PresentAttributeName中是否存在值;抱歉,如果它没有完全编译。我在记事本上写的:

编辑:


实现序列化也不难,因为您所要做的就是迭代属性字典

我知道这不是你问题的确切答案,但你可以在.NET4中使用dynamics实现一个解决方案。以下是一个例子:

public class DynamicElement : DynamicObject
{
    public Dictionary<string, object> Attributes
    {
        get { return lst; }
    }

    private Dictionary<string, object> lst;

    public DynamicElement()
    {
        lst = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase);
    }

    public bool Present(string name)
    {
        if (lst == null) return false;
        if (!lst.ContainsKey(name)) return false;

        return true;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        var name = binder.Name;
        result = null;

        if (lst == null) return false;
        if (!lst.ContainsKey(name)) return false;

        result = lst[name];
        return true;
    }
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        var name = binder.Name;

        if (lst == null) return false;

        if (!lst.ContainsKey(name))
            lst.Add(name, value);
        else
            lst[name] = value;

        return true;
    }
}
后来:

public void something(DynamicElement de)
{
    dynamic d = de;
    if(d.Done) //remember, defined this above.. just an example.
    {
        int someValue = d.AttributeValue;
    }
}
缺点是没有智能感知。这一切都在运行时解决。您还可以检查d.PresentAttributeName中是否存在值;抱歉,如果它没有完全编译。我在记事本上写的:

编辑:


实现序列化也不难,因为您所要做的就是迭代属性字典

你想做的事真的做不到。您有一个动态结构XML,其数据字段可以是任意类型,并且希望在类中有一个强类型定义。如果它是强类型的,您应该知道编译类型的类型,而您不知道@caesay的建议很好,或者简单地将数据表示为对象也可以,但如果您在编译时不知道信息,则不能进行编译器检查,即强类型。

您试图做的事情实际上无法完成。您有一个动态结构XML,其数据字段可以是任意类型,并且希望在类中有一个强类型定义。如果它是强类型的,您应该知道编译类型的类型,而您不知道@caesay的建议很好,或者简单地将数据表示为对象也可以,但是如果您在编译时不知道信息,就不能进行编译器检查,即强类型。

我很感激您的回答@caesay,我确实实现了它,但我不确定我是否需要这种类型的功能才能向字典添加多个属性。虽然我确实在代码中使用了急需的dynamo,但我尽量避免使用它

相反,我实现了以下结构以在父类中保留泛型类型:

public class AttributeValueElement : XACMLElement
{

    public AttributeValueElement()
        : base(XacmlSchema.Context)
    {

    }

    [XmlAttribute]
    public string DataType { get; set; }


    [XmlText]
    public string Value 
    { 
        get { return DataValue.GetValue().ToString(); }
        set
        {
            DataValue = AttributeValueFactory.Create(DataType, value);   
        }
    }

    public AttributeValue DataValue { get; set; }        
}


public abstract class AttributeValue
{
    public AttributeValue()
    {

    }
    public abstract object GetValue();
}

public class AttributeValue<T> : AttributeValue
{
    public T Value { get; set; }
    public override object GetValue()
    {
        return Value;
    }
}
以及相应的factory类来创建属性值:

public static AttributeValue Create(string xacmlDataType, string value)
    {

        AttributeValue _attributeValue = null;

        switch (xacmlDataType)
        {
            case "http://www.w3.org/2001/XMLSchema#string":
            case "http://www.w3.org/2001/XMLSchema#x500Name":
            case "http://www.w3.org/2001/XMLSchema#ipAddress":
            case "http://www.w3.org/2001/XMLSchema#dnsName":
            case "http://www.w3.org/2001/XMLSchema#xPathExpression":
                _attributeValue = new AttributeValue<string> { Value = value };
                break;
            case "http://www.w3.org/2001/XMLSchema#boolean":
                _attributeValue = new AttributeValue<bool> {Value = XmlConvert.ToBoolean(value) };
                break;
            case "http://www.w3.org/2001/XMLSchema#integer":
                _attributeValue = new AttributeValue<int> { Value = XmlConvert.ToInt32(value) };
                break;
            case "http://www.w3.org/2001/XMLSchema#double":
                _attributeValue = new AttributeValue<double> { Value = XmlConvert.ToDouble(value) };
                break;
            case "http://www.w3.org/2001/XMLSchema#time":
            case "http://www.w3.org/2001/XMLSchema#date":
            case "http://www.w3.org/2001/XMLSchema#dateTime":
                _attributeValue = new AttributeValue<DateTime> { Value = XmlConvert.ToDateTime(value, XmlDateTimeSerializationMode.Utc) };
                break;
            case "http://www.w3.org/2001/XMLSchema#anyURI":
                _attributeValue = new AttributeValue<Uri> { Value = new Uri(value) };
                break;
            case "http://www.w3.org/2001/XMLSchema#hexInteger":
                _attributeValue = new AttributeValue<byte[]> { Value = Encoding.ASCII.GetBytes(value) };
                break;          
            case "http://www.w3.org/2001/XMLSchema#dayTimeDuration":
            case "http://www.w3.org/2001/XMLSchema#yearMonthDuration":
                _attributeValue = new AttributeValue<TimeSpan> { Value = XmlConvert.ToTimeSpan(value) };
                break;                
            default:
                throw new NotImplementedException("Data type '" + xacmlDataType + "' is not a supported type.");
        }           

        return _attributeValue;
    }
我不想回答我自己关于stackoverflow的问题,但有时会发生


谢谢大家的回复

我很感激您的回答@caesay,我确实实现了它,但我不确定我是否需要那种类型的功能才能将多个属性添加到字典中。虽然我确实在代码中使用了急需的dynamo,但我尽量避免使用它

相反,我实现了以下结构以在父类中保留泛型类型:

public class AttributeValueElement : XACMLElement
{

    public AttributeValueElement()
        : base(XacmlSchema.Context)
    {

    }

    [XmlAttribute]
    public string DataType { get; set; }


    [XmlText]
    public string Value 
    { 
        get { return DataValue.GetValue().ToString(); }
        set
        {
            DataValue = AttributeValueFactory.Create(DataType, value);   
        }
    }

    public AttributeValue DataValue { get; set; }        
}


public abstract class AttributeValue
{
    public AttributeValue()
    {

    }
    public abstract object GetValue();
}

public class AttributeValue<T> : AttributeValue
{
    public T Value { get; set; }
    public override object GetValue()
    {
        return Value;
    }
}
以及相应的factory类来创建属性值:

public static AttributeValue Create(string xacmlDataType, string value)
    {

        AttributeValue _attributeValue = null;

        switch (xacmlDataType)
        {
            case "http://www.w3.org/2001/XMLSchema#string":
            case "http://www.w3.org/2001/XMLSchema#x500Name":
            case "http://www.w3.org/2001/XMLSchema#ipAddress":
            case "http://www.w3.org/2001/XMLSchema#dnsName":
            case "http://www.w3.org/2001/XMLSchema#xPathExpression":
                _attributeValue = new AttributeValue<string> { Value = value };
                break;
            case "http://www.w3.org/2001/XMLSchema#boolean":
                _attributeValue = new AttributeValue<bool> {Value = XmlConvert.ToBoolean(value) };
                break;
            case "http://www.w3.org/2001/XMLSchema#integer":
                _attributeValue = new AttributeValue<int> { Value = XmlConvert.ToInt32(value) };
                break;
            case "http://www.w3.org/2001/XMLSchema#double":
                _attributeValue = new AttributeValue<double> { Value = XmlConvert.ToDouble(value) };
                break;
            case "http://www.w3.org/2001/XMLSchema#time":
            case "http://www.w3.org/2001/XMLSchema#date":
            case "http://www.w3.org/2001/XMLSchema#dateTime":
                _attributeValue = new AttributeValue<DateTime> { Value = XmlConvert.ToDateTime(value, XmlDateTimeSerializationMode.Utc) };
                break;
            case "http://www.w3.org/2001/XMLSchema#anyURI":
                _attributeValue = new AttributeValue<Uri> { Value = new Uri(value) };
                break;
            case "http://www.w3.org/2001/XMLSchema#hexInteger":
                _attributeValue = new AttributeValue<byte[]> { Value = Encoding.ASCII.GetBytes(value) };
                break;          
            case "http://www.w3.org/2001/XMLSchema#dayTimeDuration":
            case "http://www.w3.org/2001/XMLSchema#yearMonthDuration":
                _attributeValue = new AttributeValue<TimeSpan> { Value = XmlConvert.ToTimeSpan(value) };
                break;                
            default:
                throw new NotImplementedException("Data type '" + xacmlDataType + "' is not a supported type.");
        }           

        return _attributeValue;
    }
我不想回答我自己关于stackoverflow的问题,但有时会发生

谢谢大家的回复