如何让XmlSerializer将bool编码为是/否?

时间:2009-03-09 05:14:30

标签: c# xml-serialization boolean

我正在将xml发送到另一个程序,该程序期望布尔标志为“是”或“否”,而不是“真”或“假”。

我的课程定义如下:

[XmlRoot()]
public class Foo {
    public bool Bar { get; set; }
}

当我序列化它时,我的输出看起来像这样:

<Foo><Bar>true</Bar></Foo>

但我希望如此:

<Foo><Bar>yes</Bar></Foo>

我可以在序列化时这样做吗?我宁愿不必诉诸于此:

[XmlRoot()]
public class Foo {
    [XmlIgnore()]
    public bool Bar { get; set; }

    [XmlElement("Bar")]
    public string BarXml { get { return (Bar) ? "yes" : "no"; } }
}

请注意,我还希望能够反序列化此数据。

7 个答案:

答案 0 :(得分:22)

好的,我一直在研究这个问题。以下是我的想法:

// 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

换句话说,你可以像对待一样对待它。

然后!它序列化为:

<Foo><Bar>yes</Bar></Foo>

它也正确地反序列化。

可能有一些方法可以让我的XmlSerializer自动将它遇到的任何bool转换为YesNo,但我还没有找到它。任何人吗?

答案 1 :(得分:8)

很简单。使用代理财产。在实际属性上应用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!"); }
        }

    }

点击此处查看full compilable source example

答案 2 :(得分:3)

将bool值序列化为“yes”或“no”会使数据类型完全变为布尔值。相反,你可以添加一个单独的属性来计算一个布尔值,并根据它的数据类型返回“是”或“否”吗?也许你甚至可以通过使返回类型成为仅指定这些值的枚举来强制“是”或“否”。

public YesOrNo DoYouLoveIt
{
    get { return boolToEvaluate ? YesOrNo.Yes : YesOrNo.No; }
}

这可能有点矫枉过正,但可能会满足您的需求。我为这样一个简单的值提出枚举的唯一原因是你要限制值而不是允许任何字符串。

答案 3 :(得分:2)

我使用属性方法,但不是检查字符串是否等于是,我更喜欢检查字符串是否以(不区分大小写)“YT1”开头。这允许文件包含true,True,t,T,y,Y,yes,Yes,1等所有将评估为true的文件。虽然我可以指定false为false,False,f,F,n,N,no,No,0等,但任何与true不匹配的值仍然会计算为false。

答案 4 :(得分:0)

您的属性示例可能是您可以执行此操作的最简单方法。如果它有帮助,我相信你不需要把它变成公共属性,因为该属性在你背后的类上实现了ISerializable。要启用反序列化,您应该能够实现set { Bar = value == "yes"; }

答案 5 :(得分:-1)

@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方法,框架不处理包装元素   自动。您的实施必须这样做。没有观察   这些定位规则可能导致代码生成意外的运行时   异常或损坏的数据。

答案 6 :(得分:-2)

您需要做的事情听起来更像是显示问题。如果您的应用程序允许,最好将数据类型保留为布尔值,并在用户界面中显示“是/否”。