【问题标题】:Serialize nullable type to optional non-nillable element将可空类型序列化为可选的不可空元素
【发布时间】:2014-01-26 20:42:32
【问题描述】:

我有一个 xsd 架构,其中包含 int 类型的可选元素(minOccurs=0maxOccurs=1)。该元素未定义为 nillable。在数据模型中,我想将其映射到 .net 类型 Nullable<int> 的字段,其中 null 值应对应于 xml 中省略的元素。

但是,使用XmlSerializer,我似乎必须在数据模型中用[XmlElement IsNullable=true] 声明一个可为空的字段。如果我设置IsNullable=false,我会得到异常“IsNullable 可能不会设置为 Nullable 类型的 'false'。”IsNullable 可能不会设置为 Nullable 类型的 'false'。考虑使用 'System.Int32' 类型或从 XmlElement 属性中删除 IsNullable 属性。“但如果我理解正确,设置 IsNullable=true(或忽略该属性)会隐式地将元素设置为 nillable,因此更改架构。

这是架构优先的设计,所以我不能只在架构中的元素中添加“nillable”。

如何将可为空的 .net 类型映射到不可为空的 xml 元素?

(我知道在序列化为 xml 时可以通过在数据模型中使用 XxxSpecified 属性省略 nil-elements,但据我所知,这种方法仍然需要向 xsd 架构添加 nillable。)

编辑:感谢 cmets,我现在更好地理解了这个问题。实际上有两个不同的问题:

  1. 像 xsd.exe 这样的架构到代码生成器会创建一个不可为空的 如果架构元素不可为空,则输入生成的模型 (即使它是可选的)。我可以覆盖它吗(使用任何已知的代码 生成器),所以我在生成的代码中获得了可为空的类型?

  2. XmlSerializer 要求数据模型中的可为空类型具有 [XmlElement IsNullable=true],表示模型隐式添加 'nillable' 到模式。我可以避免这种情况吗?

【问题讨论】:

  • 到底是什么问题?您是从代码生成 xsd,反之亦然,还是两者都不生成?
  • 我正在从 XSD 生成代码。我的问题是:我不知道是否可以将 C# 映射中的可为空类型映射到 xml 中的非 nillabale 元素。由于这是架构优先的设计,我不能只在架构中的元素中添加“nillable”。
  • 呃。我没有把我的问题说清楚。您是使用自动工具 XSD.exe 从 XSD 生成代码,还是手动编写代码?如果是前者,您唯一的解决方案是添加某种预处理或后处理步骤,因为 XSD.exe 不支持您想要的。如果是后者,您的选择范围会更广。
  • 其实我是用Xsd2Code来生成代码的,所以我可以在一定程度上修改代码的生成方式。但是我不确定如何在不引入可空元素的情况下使用手工编写的代码来解决这个问题。
  • 您是否考虑过使用 FormatterServices 对象和 System.Reflection 命名空间编写自己的序列化程序?我有一个例子,但它不是微不足道的。我已经花了很多年的时间来解决这个问题,但那是为我自己的。如果您有特定要求,则需要对其进行描述。

标签: .net xml-serialization


【解决方案1】:

我不确定 XxxSpecified,但您可以使用 ShouldSerializeXxx 方法。无论属性类型是否可以为空,这些都可以愉快地工作。以下应该可以完成这项工作:

public int? Property { get ; set ; }

// this member is used for XML serialization
public bool ShouldSerializeProperty () { return Property.HasValue ; }

至于从 XSD 架构生成代码,如果您使用 Microsoft 的 xsd.exe 工具,最好的选择似乎是使用例如后处理生成的程序集。 Mono.Cecil 修改感兴趣的属性的类型并插入任何额外的序列化相关成员,如ShouldSerializeXxx。添加 XSLT 预处理步骤以在具有可空元素声明的“固定”模式上运行 xsd.exe 可以实现第一个目标,但不能实现第二个目标。 xsd.exe 不够灵活,无法满足您的需求。您也可以尝试将此功能添加到 xsd2code,因为它是开源的。

【讨论】:

  • 如果我理解正确,这相当于在属性上有一个 [XmlElement IsNullable=true] 属性,从而允许在生成和使用的 xml 中包含 nil 元素,这反过来意味着我偏离了xsd 架构。但由于我可以避免输出 nil 元素,这可能是一个可以接受的解决方案。
  • 不,它不等同。 [XmlElement (IsNullable=true)] 对类型已经是 Nullable<> 的属性没有影响,因为它已经被属性类型隐含了。在这种情况下你甚至不能设置IsNullable=falseXmlSerializer 会抛出异常。您是对的,此声明允许序列化程序使用 nil XML 元素,但如果 XML 符合您的 XSD 架构,则不应该有任何内容,所以我看不出问题出在哪里。
【解决方案2】:

我前段时间也遇到过这个问题。我通过引入处理序列化逻辑的附加属性解决了这个问题。

  1. 首先,您使用 [XmlIgnore] 属性标记您的原始属性,以将其从序列化/反序列化中排除。

    // Mark your original property from serialization/deserialization logic
    [XmlIgnore]
    public int? OriginalProperty { get; set; }
    
  2. 然后添加附加属性并用 [XmlElement("YourElementName")] 属性标记它以处理序列化逻辑。

    // Move serialization logic to additional string property
    [XmlElement("OriginalPropertyXmlElement")]
    public string OriginalPropertyAsString
    {
        get
        {
            //...
        }
        set
        {
            //...
        }
    }
    
  3. 在反序列化时它将:

    set
    {
        // Check the field existence in xml
        if (string.IsNullOrEmpty(value))
        {
            // Set the original property to null
            this.OriginalProperty = default(int?);
        }
        else
        {
            // Get value from xml field
            this.OriginalProperty = int.Parse(value);
        }
    }
    
  4. 关于序列化:

    get
    {
        if (this.OriginalProperty.HasValue)
        {
            // Serialize it
            return this.OriginalProperty.ToString();
        }
        else
        {
            // Don't serialize it
            return null;
        }
    }
    
  5. 示例可能如下所示:

    [XmlRoot("scene")]
    public class Scene
    {
        [XmlIgnore]
        public int? ParentId { get; set; }
    
        [XmlElement("parent_id")]
        public string ParentIdAsString
        {
            get
            {
                return this.ParentId.HasValue ? this.ParentId.ToString() : null;
            }
    
            set
            {
                this.ParentId = !string.IsNullOrEmpty(value) ? int.Parse(value) : default(int?);
            }
        }
    }
    

这非常简单,您不必编写自己的序列化程序,也不必破解 xsd 或其他东西。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-09
    • 1970-01-01
    • 2010-10-10
    • 2010-10-21
    • 2022-01-22
    相关资源
    最近更新 更多