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

我在反序列化具有布尔值的 XML 文件时遇到问题。我正在反序列化的源 XML 文件是从 VB6 应用程序创建的,其中所有布尔值都大写(TrueFalse)。当我尝试反序列化 XML 时,我得到了一个

System.FormatException: The string 'False' is not a valid Boolean value.

有没有办法用属性说忽略大小写?

【问题讨论】:

    标签: c# xml xml-serialization boolean


    【解决方案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(),但除此之外,这在我的初始测试中确实按预期工作。
            猜你喜欢
            • 2015-03-30
            • 2012-10-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-01-05
            • 1970-01-01
            • 2017-01-11
            相关资源
            最近更新 更多