如何让XmlSerializer将bool编码为是/否?
我将xml发送到另一个程序,它希望布尔标志为“是”或“否”,而不是“真”或“假”。
我有一个类定义如下:
[XmlRoot()] public class Foo { public bool Bar { get; set; } }
当我序列化它时,我的输出如下所示:
true
但我希望它是这样的:
yes
我可以在序列化时这样做吗? 我宁愿不必诉诸于此:
[XmlRoot()] public class Foo { [XmlIgnore()] public bool Bar { get; set; } [XmlElement("Bar")] public string BarXml { get { return (Bar) ? "yes" : "no"; } } }
请注意,我还希望能够再次反序列化此数据。
好的,我一直在研究这个问题。 这就是我提出的:
// use this instead of a bool, and it will serialize to "yes" or "no" // minimal example, not very robust public struct YesNo : IXmlSerializable { // we're just wrapping a bool private bool Value; // allow implicit casts to/from bool public static implicit operator bool(YesNo yn) { return yn.Value; } public static implicit operator YesNo(bool b) { return new YesNo() {Value = b}; } // implement IXmlSerializable public XmlSchema GetSchema() { return null; } public void ReadXml(XmlReader reader) { Value = (reader.ReadElementContentAsString() == "yes"); } public void WriteXml(XmlWriter writer) { writer.WriteString((Value) ? "yes" : "no"); } }
然后我将我的Foo类更改为:
[XmlRoot()] public class Foo { public YesNo Bar { get; set; } }
请注意,因为YesNo
可以隐式地转换为bool
(反之亦然),所以你仍然可以这样做:
Foo foo = new Foo() { Bar = true; }; if ( foo.Bar ) { // ... etc
换句话说,你可以像对待bool一样对待它。
而且! 它序列化为:
yes
它也正确地反序列化。
可能有一些方法可以让我的XmlSerializer自动将遇到的任何bool
为YesNo
s,但我还没有找到它。 任何人?
非常简单。 使用代理财产。 在实际属性上应用XmlIgnore。 代理是一个字符串,必须使用带有元素名称覆盖的XmlElement属性。 在覆盖中指定实际属性的名称。 代理属性根据实际属性的值进行不同的序列化。 您还必须为Surrogate提供一个setter,并且setter应该适当地设置实际属性,无论它序列化什么值。 换句话说,它需要双向进行。
剪断:
public class SomeType { [XmlElement] public int IntValue; [XmlIgnore] public bool Value; [XmlElement("Value")] public string Value_Surrogate { get { return (Value)? "Yes, definitely!":"Absolutely NOT!"; } set { Value= (value=="Yes, definitely!"); } } }
单击此处获取完整的可编译源示例 。
将bool值序列化为“yes”或“no”会将数据类型更改为布尔值。 相反,你可以添加一个单独的属性来评估一个布尔值,并根据它的数据类型返回“是”或“否”吗? 也许您甚至可以通过使返回类型为仅指定这些值的枚举来强制“是”或“否”。
public YesOrNo DoYouLoveIt { get { return boolToEvaluate ? YesOrNo.Yes : YesOrNo.No; } }
这可能有点矫枉过正,但可能会满足您的需求。 我为这样一个简单的值提出枚举的唯一原因是你要限制值而不是允许任何字符串。
我使用属性方法,但不是检查字符串是否等于是,我更喜欢检查字符串是否以(不区分大小写)“YT1”开头。 这允许文件包含true,True,t,T,y,Y,yes,Yes,1等所有将评估为true的文件。 虽然我可以指定false为false,False,f,F,n,N,no,No,0等,但任何与true不匹配的值仍为false。
您的属性示例可能是您可以执行此操作的最简单方法。 如果它有帮助,我相信你不需要把它变成公共属性,因为该属性在你背后的类上实现了ISerializable。 要启用反序列化,您应该能够实现set { Bar = value == "yes"; }
set { Bar = value == "yes"; }
那么实现OnSerializing和OnDeserializing方法呢?
您需要做的事情听起来更像是显示问题。 如果您的应用程序允许,最好将数据类型保留为布尔值,并在用户界面中显示“是/否”。
@Blorgbeard:如果在对象类中有多个这样的YesNo类,请确保读取整个元素。
public void ReadXml(XmlReader reader) { string element = reader.ReadOuterXml(); int startIndex = element.IndexOf('>') + 1; int length = element.LastIndexOf('<') - startIndex; string text = (element.Substring(startIndex, length).ToLowerInvariant(); Value = (text == "yes"); }
否则这可能会导致问题。
ReadXml方法必须使用WriteXml方法写入的信息重新构建对象。
调用此方法时,读取器位于包含类型信息的元素的开头。 也就是说,就在指示序列化对象开始的开始标记之前。 当此方法返回时,它必须从头到尾读取整个元素,包括其所有内容。 与WriteXml方法不同,框架不会自动处理包装元素。 您的实施必须这样做。 如果不遵守这些定位规则,可能会导致代码生成意外的运行时exception或损坏的数据。