Xml 序列化与“真”和“假”

Posted

技术标签:

【中文标题】Xml 序列化与“真”和“假”【英文标题】:Xml Serialization vs. "True" and "False" 【发布时间】:2010-11-12 10:01:06 【问题描述】:

我在反序列化具有布尔值的 XML 文件时遇到问题。我正在反序列化的源 XML 文件是从 VB6 应用程序创建的,其中所有布尔值都大写(TrueFalse)。当我尝试反序列化 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 序列化与“真”和“假”的主要内容,如果未能解决你的问题,请参考以下文章

图像生成器中的错误:要求检索元素 0,但序列的长度为 0

如何让 XmlSerializer 将布尔值编码为是/否?

是否有一个 XML 或 JSON 序列化器知道使用哪个构造函数来填充不可变对象并序列化 IEnumerable<> 属性? [关闭]

在.net中序列化读写xml方法的总结

使用XStream是实现XML与Java对象的转换--持久化

Code-Serialization:Xml序列化与Xml反序列化