Xml 序列化与“真”和“假”
Posted
技术标签:
【中文标题】Xml 序列化与“真”和“假”【英文标题】:Xml Serialization vs. "True" and "False" 【发布时间】:2010-11-12 10:01:06 【问题描述】:我在反序列化具有布尔值的 XML 文件时遇到问题。我正在反序列化的源 XML 文件是从 VB6 应用程序创建的,其中所有布尔值都大写(True
、False
)。当我尝试反序列化 XML 时,我得到了一个
System.FormatException: The string 'False' is not a valid Boolean value.
有没有办法用属性说忽略大小写?
【问题讨论】:
【参考方案1】:我认为没有。您可以将其设为字符串并通过将 ignoreCase 值设置为 true 来比较 (String.Compare)。
【讨论】:
【参考方案2】:没有。 XML Serializer 与 XML Schema 一起使用,“True”和“False”不是有效的布尔值。
您可以使用 XML 转换来转换这两个值,也可以实现 IXmlSerializable 接口并自行进行序列化和反序列化。
【讨论】:
你是对的,约翰,当你在吃午饭前回答时会发生这种情况:\..但公平地说,你对 Xml Transform 的建议也可能会转换一个不打算成为布尔值的值。 其实,没有。我会让他只明确引用布尔值的属性和/或元素。我并不是要转换每个属性。【参考方案3】:您可以将该值作为字符串读取到字符串字段中,然后有一个只读 bool 字段,其中包含 if 语句以返回 bool true 或 false。
例如(使用 c#):
public bool str2bool(string str)
if (str.Trim().ToUpper() == "TRUE")
return true;
else
return false;
你可以在模板中使用它:
<xsl:if test="user:str2bool($mystr)">
【讨论】:
你应该包括一个这样的例子。如果你这样做,你可能会得到最好的建议。【参考方案4】:不要使用 True 或 False,而是使用 0 或 1。它适用于布尔值。
【讨论】:
补充一点:小写“true”和“false”也可以【参考方案5】:基于another stack overflow question,你可以这样做:
public class MySerilizedObject
[XmlIgnore]
public bool BadBoolField get; set;
[XmlElement("BadBoolField")]
public string BadBoolFieldSerialize
get return this.BadBoolField ? "True" : "False";
set
if(value.Equals("True"))
this.BadBoolField = true;
else if(value.Equals("False"))
this.BadBoolField = false;
else
this.BadBoolField = XmlConvert.ToBoolean(value);
【讨论】:
【参考方案6】:我偶然发现了同样的问题,根据 jman 的回答,我是这样解决的:
[XmlIgnore]
public bool BadBoolField get; set;
[XmlAttribute("badBoolField")]
public string BadBoolFieldSerializable
get
return this.BadBoolField.ToString();
set
this.BadBoolField= Convert.ToBoolean(value);
请注意,这不一定是 XML / 序列化规范,但它工作得很好,它可以处理广泛的转换值(即字符串,如“True”、“true”,如果你将替换为字符串的约束它也可以处理数字)。
【讨论】:
【参考方案7】:这是我根据发现的其他一些问题提出的更简洁的解决方案。它更简洁,因为除了将类型声明为 SafeBool 之外,您不需要在代码中进行任何操作,如下所示:
public class MyXMLClass
public SafeBool Bool get; set;
public SafeBool? OptionalBool get; set;
您甚至可以将它们设为可选,这一切都可以正常工作。此 SafeBool 结构将处理 true/false、yes/no 或 y/n 的任何情况变体。它将始终序列化为真/假,但是我有其他类似于此的结构,当架构需要时,我使用它专门序列化为 y/n 或 yes/no(即:BoolYN、BoolYesNo 结构)。
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace AMain.CommonScaffold
public struct SafeBool : IXmlSerializable
private bool _value;
/// <summary>
/// Allow implicit cast to a real bool
/// </summary>
/// <param name="yn">Value to cast to bool</param>
public static implicit operator bool(
SafeBool yn)
return yn._value;
/// <summary>
/// Allow implicit cast from a real bool
/// </summary>
/// <param name="b">Value to cash to y/n</param>
public static implicit operator SafeBool(
bool b)
return new SafeBool _value = b ;
/// <summary>
/// This is not used
/// </summary>
public XmlSchema GetSchema()
return null;
/// <summary>
/// Reads a value from XML
/// </summary>
/// <param name="reader">XML reader to read</param>
public void ReadXml(
XmlReader reader)
var s = reader.ReadElementContentAsString().ToLowerInvariant();
_value = s == "true" || s == "yes" || s == "y";
/// <summary>
/// Writes the value to XML
/// </summary>
/// <param name="writer">XML writer to write to</param>
public void WriteXml(
XmlWriter writer)
writer.WriteString(_value ? "true" : "false");
【讨论】:
查看我的搜索和替换答案。您有很多代码可能具有更好的 O(n),但有时问题只需要 Ross Perot 解决方案。打开引擎盖并修复它。 我喜欢这个解决方案,但为了完整起见,我还实现了一些其他接口、运算符和方法。我将编辑您的答案以包含它们(我还将结构重命名为 FlexibleBool)和它们的单元测试。【参考方案8】:不要费心修复损坏的 xml 系统或与 XmlSerializer 作斗争,尤其是对于如此微不足道的事情。这不值得。 VB6 不会很快回来。
相反,在反序列化之前获取文档并更改值。如果您担心在标签之外更改它们,请使用正则表达式或在值中包含尖括号。
xml = xml.Replace("True", "true").Replace("False", "false");
它不会因为优雅而赢得任何奖项,但它会让你重新开始工作。有时你只需要蓝领就行了。
至于性能,是的,您正在重复字符串 O(n),但由于替换字符串的长度相同,因此不需要任何移动的字符串元素。此外,根据实现的不同,修改 XmlSerializer 可能会产生更大的开销。
【讨论】:
您假设您可以轻松访问原始 html 以便能够进行搜索和替换,因此您的答案不是一个好答案。更不用说如果 XML 数据包很大,您的代码将产生两次巨大的临时字符串,因为 .net 中的字符串是不可变的。 @KendallBennett 那么你不是也假设他可以访问课程吗?我从未见过无需访问 xml 输入就可以反序列化数据的情况。如果你真的关心性能和内存,你真的应该避免使用 XmlSerializer,因为它使用反射,这是 .NET 中最昂贵的操作之一。我的解决方案没有任何内容表明它不能处理流数据。我的观点是,幼稚的实现有时是最好的。我已经学会了这一点。 您不能简单地阻止将 XML 中为 True 的所有内容替换为 true,而将 False 替换为 false。这当然是幼稚的,并且肯定会在某种类型的反应上打破。如果其中存储了一些区分大小写且包含 True 字样的内容怎么办?突然之间不再起作用了…… 是的,这就是为什么我提到搜索类似 ">False 【参考方案9】:在特殊情况下有一个非常简单和简短的解决方案。
我今天遇到了类似的问题,外部给定的 XML 文件包含值 TRUE/FALSE,它们应该具有布尔含义。
如果对于一个应用程序来说,反序列化的文档包含本机布尔值不是强制性的,而只是将其反序列化为受限于任何两个替代值的东西,那么可以简单地使用枚举(这里的属性为举例):
public enum BOOL FALSE, TRUE;
public MyClass
[XmlAttribute]
public BOOL MyStrangeBooleanAttribute get; set;
这样的元素只会反序列化,没有任何问题
<MyClass MyStrangeBooleanAttribute = "TRUE" />
当然不可能在代码中使用该属性进行直接布尔运算,例如
if (MyStrangeBooleanAttribute) // ... doesn't work
我认为可以通过定义隐式转换来处理这个问题,但我没有测试它,因为我不需要它。
【讨论】:
【参考方案10】:我有一个包含许多布尔值的 xml,我不想最终拥有这么多重复的布尔属性,所以我尝试了一种不同的方法来提供自定义 xml 阅读器来完成这项工作:
public class MyXmlReader : XmlTextReader
public MyXmlReader(TextReader reader) : base(reader)
public override string ReadElementString()
var text = base.ReadElementString();
// bool TryParse accepts case-insensitive 'true' and 'false'
if (bool.TryParse(text, out bool result))
text = XmlConvert.ToString(result);
return text;
并用于:
using (var sr = new StringReader(text))
using (var r = new MyXmlReader(sr))
var result = serializer.Deserialize(r);
【讨论】:
我在 .NET Core 2.2 中,必须覆盖ReadElementContentAsString()
而不是 ReadElementString()
,但除此之外,这在我的初始测试中确实按预期工作。以上是关于Xml 序列化与“真”和“假”的主要内容,如果未能解决你的问题,请参考以下文章
是否有一个 XML 或 JSON 序列化器知道使用哪个构造函数来填充不可变对象并序列化 IEnumerable<> 属性? [关闭]