.net 使用 XmlSerializer 将空的 xml 属性值反序列化为可为空的 int 属性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1295697/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Deserializing empty xml attribute value into nullable int property using XmlSerializer
提问by Aliaksei Kliuchnikau
I get an xml from the 3rd party and I need to deserialize it into C# object. This xml may contain attributes with value of integer type or empty value: attr=”11” or attr=””. I want to deserialize this attribute value into the property with type of nullable integer. But XmlSerializer does not support deserialization into nullable types. The following test code fails during creation of XmlSerializer with InvalidOperationException {"There was an error reflecting type 'TestConsoleApplication.SerializeMe'."}.
我从第 3 方获得了一个 xml,我需要将它反序列化为 C# 对象。此 xml 可能包含值为整数类型或空值的属性:attr=”11” 或 attr=””。我想将此属性值反序列化为可空整数类型的属性。但是 XmlSerializer 不支持反序列化为可空类型。以下测试代码在创建 XmlSerializer 期间失败,并带有 InvalidOperationException {"There was an error reflect type 'TestConsoleApplication.SerializeMe'."}。
[XmlRoot("root")]
public class SerializeMe
{
[XmlElement("element")]
public Element Element { get; set; }
}
public class Element
{
[XmlAttribute("attr")]
public int? Value { get; set; }
}
class Program {
static void Main(string[] args) {
string xml = "<root><element attr=''>valE</element></root>";
var deserializer = new XmlSerializer(typeof(SerializeMe));
Stream xmlStream = new MemoryStream(Encoding.ASCII.GetBytes(xml));
var result = (SerializeMe)deserializer.Deserialize(xmlStream);
}
}
When I change type of 'Value' property to int, deserialization fails with InvalidOperationException:
当我将“值”属性的类型更改为 int 时,反序列化失败并显示 InvalidOperationException:
There is an error in XML document (1, 16).
XML 文档中存在错误 (1, 16)。
Can anybody advise how to deserialize attribute with empty value into nullable type (as a null) at the same time deserializing non-empty attribute value into the integer? Is there any trick for this so I will not have to do deserialization of each field manually (actually there are a lot of them)?
任何人都可以建议如何将具有空值的属性反序列化为可空类型(作为空值),同时将非空属性值反序列化为整数?有什么技巧可以让我不必手动对每个字段进行反序列化(实际上有很多)?
Update after comment from ahsteele:
ahsteele 评论后更新:
As far as I know, this attribute works only with XmlElementAttribute - this attribute specifies that the element has no content, whether child elements or body text. But I need to find the solution for XmlAttributeAttribute. Anyway I cannot change xml because I have no control over it.
This property works only when attribute value is non-empty or when attribute is missing. When attr has empty value (attr='') the XmlSerializer constructor fails (as expected).
public class Element { [XmlAttribute("attr")] public int Value { get; set; } [XmlIgnore] public bool ValueSpecified; }Custom Nullable class like in this blog post by Alex Scordellis
I tried to adopt the class from this blog post to my problem:
[XmlAttribute("attr")] public NullableInt Value { get; set; }But XmlSerializer constructor fails with InvalidOperationException:
Cannot serialize member 'Value' of type TestConsoleApplication.NullableInt.
XmlAttribute/XmlText cannot be used to encode types implementing IXmlSerializable }
Ugly surrogate solution (shame on me that I wrote this code here :) ):
public class Element { [XmlAttribute("attr")] public string SetValue { get; set; } public int? GetValue() { if ( string.IsNullOrEmpty(SetValue) || SetValue.Trim().Length <= 0 ) return null; int result; if (int.TryParse(SetValue, out result)) return result; return null; } }But I don't want to come up with the solution like this because it breaks interface of my class for its consumers. I would better manually implement IXmlSerializable interface.
据我所知,此属性仅适用于 XmlElementAttribute - 此属性指定元素没有内容,无论是子元素还是正文文本。但我需要找到 XmlAttributeAttribute 的解决方案。无论如何,我无法更改 xml,因为我无法控制它。
此属性仅在属性值非空或属性缺失时有效。当 attr 具有空值 (attr='') 时,XmlSerializer 构造函数失败(如预期)。
public class Element { [XmlAttribute("attr")] public int Value { get; set; } [XmlIgnore] public bool ValueSpecified; }自定义 Nullable 类,如 Alex Scordellis 的这篇博文
我试图将这篇博客文章中的课程应用于我的问题:
[XmlAttribute("attr")] public NullableInt Value { get; set; }但 XmlSerializer 构造函数因 InvalidOperationException 而失败:
无法序列化 TestConsoleApplication.NullableInt 类型的成员“Value”。
XmlAttribute/XmlText 不能用于编码实现 IXmlSerializable 的类型 }
丑陋的代理解决方案(我很惭愧,我在这里写了这段代码:)):
public class Element { [XmlAttribute("attr")] public string SetValue { get; set; } public int? GetValue() { if ( string.IsNullOrEmpty(SetValue) || SetValue.Trim().Length <= 0 ) return null; int result; if (int.TryParse(SetValue, out result)) return result; return null; } }但我不想提出这样的解决方案,因为它破坏了我的类为其消费者的接口。我最好手动实现 IXmlSerializable 接口。
Currently it looks like I have to implement IXmlSerializable for the whole Element class (it is big) and there are no simple workaround…
目前看起来我必须为整个 Element 类(它很大)实现 IXmlSerializable 并且没有简单的解决方法......
采纳答案by Aliaksei Kliuchnikau
I solved this problem by implementing IXmlSerializable interface. I did not found easier way.
我通过实现 IXmlSerializable 接口解决了这个问题。我没有找到更简单的方法。
Here is the test code sample:
这是测试代码示例:
[XmlRoot("root")]
public class DeserializeMe {
[XmlArray("elements"), XmlArrayItem("element")]
public List<Element> Element { get; set; }
}
public class Element : IXmlSerializable {
public int? Value1 { get; private set; }
public float? Value2 { get; private set; }
public void ReadXml(XmlReader reader) {
string attr1 = reader.GetAttribute("attr");
string attr2 = reader.GetAttribute("attr2");
reader.Read();
Value1 = ConvertToNullable<int>(attr1);
Value2 = ConvertToNullable<float>(attr2);
}
private static T? ConvertToNullable<T>(string inputValue) where T : struct {
if ( string.IsNullOrEmpty(inputValue) || inputValue.Trim().Length == 0 ) {
return null;
}
try {
TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));
return (T)conv.ConvertFrom(inputValue);
}
catch ( NotSupportedException ) {
// The conversion cannot be performed
return null;
}
}
public XmlSchema GetSchema() { return null; }
public void WriteXml(XmlWriter writer) { throw new NotImplementedException(); }
}
class TestProgram {
public static void Main(string[] args) {
string xml = @"<root><elements><element attr='11' attr2='11.3'/><element attr='' attr2=''/></elements></root>";
XmlSerializer deserializer = new XmlSerializer(typeof(DeserializeMe));
Stream xmlStream = new MemoryStream(Encoding.ASCII.GetBytes(xml));
var result = (DeserializeMe)deserializer.Deserialize(xmlStream);
}
}
回答by Aliaksei Kliuchnikau
This should work:
这应该有效:
[XmlIgnore]
public int? Age { get; set; }
[XmlElement("Age")]
public string AgeAsText
{
get { return (Age.HasValue) ? Age.ToString() : null; }
set { Age = !string.IsNullOrEmpty(value) ? int.Parse(value) : default(int?); }
}
回答by ahsteele
I've been messing around with serialization a lot myself of late and have found the following articles and posts helpful when dealing with null data for value types.
我最近一直在忙于序列化,并且发现以下文章和帖子在处理值类型的空数据时很有帮助。
The answer to How to make a value type nullable with XmlSerializer in C# - serializationdetails a pretty nifty trick of the XmlSerializer. Specifically, the XmlSerialier looks for a XXXSpecified boolean property to determine if it should be included which allows you to ignore nulls.
如何在 C# 中使用 XmlSerializer 使值类型可以为空的答案- 序列化详细介绍了 XmlSerializer 的一个非常漂亮的技巧。具体来说, XmlSerialier 查找 XXXSpecified 布尔属性以确定是否应该包含它,从而允许您忽略空值。
Alex Scordellis asked a StackOverflow question which received a good answer. Alex also did a good writeup on his blog about the problem he was trying to solve Using XmlSerializer to deserialize into a Nullable<int>.
Alex Scordellis 问了一个 StackOverflow 问题,得到了很好的回答。Alex 还在他的博客上写了一篇关于他试图解决使用 XmlSerializer 反序列化为 Nullable<int> 的问题的好文章。
The MSDN documentation on Xsi:nil Attribute Binding Supportis also useful. As is the documentation on IXmlSerializable Interface, though writing your own implementation should be your last resort.
Xsi:nil Attribute Binding Support上的 MSDN 文档也很有用。与IXmlSerializable Interface上的文档一样,尽管编写自己的实现应该是最后的手段。
回答by Levi Fuller
Thought I might as well throw my answer into the hat: Solved this issue by creating a custom type that implements the IXmlSerializable interface:
我想我不妨把我的答案扔进帽子里:通过创建一个实现 IXmlSerializable 接口的自定义类型来解决这个问题:
Say you have a an XML object with the following nodes:
假设您有一个包含以下节点的 XML 对象:
<ItemOne>10</Item2>
<ItemTwo />
The object to represent them:
代表他们的对象:
public class MyItems {
[XmlElement("ItemOne")]
public int ItemOne { get; set; }
[XmlElement("ItemTwo")]
public CustomNullable<int> ItemTwo { get; set; } // will throw exception if empty element and type is int
}
Dynamic nullable struct to represent any potential nullable entries along with conversion
动态可为空结构,用于表示任何潜在的可为空条目以及转换
public struct CustomNullable<T> : IXmlSerializable where T: struct {
private T value;
private bool hasValue;
public bool HasValue {
get { return hasValue; }
}
public T Value {
get { return value; }
}
private CustomNullable(T value) {
this.hasValue = true;
this.value = value;
}
public XmlSchema GetSchema() {
return null;
}
public void ReadXml(XmlReader reader) {
string strValue = reader.ReadString();
if (String.IsNullOrEmpty(strValue)) {
this.hasValue = false;
}
else {
T convertedValue = strValue.To<T>();
this.value = convertedValue;
this.hasValue = true;
}
reader.ReadEndElement();
}
public void WriteXml(XmlWriter writer) {
throw new NotImplementedException();
}
public static implicit operator CustomNullable<T>(T value) {
return new CustomNullable<T>(value);
}
}
public static class ObjectExtensions {
public static T To<T>(this object value) {
Type t = typeof(T);
// Get the type that was made nullable.
Type valueType = Nullable.GetUnderlyingType(typeof(T));
if (valueType != null) {
// Nullable type.
if (value == null) {
// you may want to do something different here.
return default(T);
}
else {
// Convert to the value type.
object result = Convert.ChangeType(value, valueType);
// Cast the value type to the nullable type.
return (T)result;
}
}
else {
// Not nullable.
return (T)Convert.ChangeType(value, typeof(T));
}
}
}

