17

我正在将 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"; } }
}

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

4

7 回答 7

24

好的,我一直在研究这个。这是我想出的:

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

换句话说,您可以将其视为布尔值。

和w00t!它序列化为:

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

它还可以正确反序列化。

可能有某种方法可以让我的 XmlSerializer 自动将bool它遇到的任何 s 转换为YesNos - 但我还没有找到它。任何人?

于 2009-03-09T08:51:16.457 回答
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!"); }
        }

    }

单击此处获取完整的可编译源示例

于 2009-03-09T05:37:28.917 回答
3

将 bool 值序列化为“yes”或“no”会完全改变数据类型而不是 boolean。相反,您是否可以添加一个单独的属性来评估布尔值并根据其数据类型返回“是”或“否”?也许您甚至可以通过将返回类型设置为仅指定这些值的枚举来强制“是”或“否”。

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

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

于 2009-03-09T05:18:50.913 回答
2

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

于 2012-08-30T00:19:53.430 回答
0

您的财产示例可能是您可以做到的最简单的方法。如果有帮助,我相信您不需要将其设为公共属性,因为该属性在您背后的类上实现 ISerializable。要启用反序列化,您应该能够实现set { Bar = value == "yes"; }

于 2009-03-09T05:38:10.413 回答
0

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

于 2012-11-26T08:44:41.523 回答
-2

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

于 2009-03-09T05:23:20.170 回答