如何从 XML 反序列化未知类型
本文关键字:未知 类型 反序列化 XML | 更新日期: 2023-09-27 18:35:18
考虑以下过于简化的XML块:
<ElementA>
<AttributeValue DataType="http://www.w3.org/2001/XMLSchema#integer">5
</AttributeValue>
</ElementA>
具体来说,从 DataType 属性查看 AttributeValue 元素,我知道我的值是整数类型(尽管它可能是双精度、字符串、日期时间......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 属性并将值存储为字符串,然后稍后转换,但在我的业务对象中实际拥有正确的类型以供以后处理会很有帮助
感谢您的任何帮助!
杰森
好吧,我知道这不是您问题的确切答案,但是您可以使用.Net 4中的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;
}
}
然后使用它,它将类似于这样:
dynamic d = new DynamicElement();
d.AttributeValue = Convert.ToInt32(xmlElement.Value);
d.Done = true; //just another example.
后:
public void something(DynamicElement de)
{
dynamic d = de;
if(d.Done) //remember, defined this above.. just an example.
{
int someValue = d.AttributeValue;
}
}
缺点是不会有智能感。这一切都在运行时解决。您还可以检查值是否存在于d.Present("AttributeName");
抱歉(如果它没有完全编译)。我把它写在记事本上:)
编辑:
实现序列化也应该不难 - 因为您所要做的就是迭代属性字典。
感谢您@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;
}
}
并用相应的工厂类来创建属性值:
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;
}
我讨厌回答我自己关于堆栈溢出的问题,但有时会发生这种情况。
感谢您的回复!
你想做的事情真的做不到。您有一个动态结构(其数据字段可以是任意类型的 XML),并且您希望在类中具有强类型定义。如果要使用强类型,您应该知道编译类型的类型,而您不知道。@caesay的建议是一个很好的建议,或者简单地将数据表示为Object
也可以,但是如果您在编译时不知道信息,则无法进行编译器检查(即强类型)。