将可空类型序列化为可选的非可贵元素

时间:2014-01-08 16:14:12

标签: .net xml-serialization

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

但是,使用XmlSerializer,似乎我 在数据模型中使用[XmlElement IsNullable=true]声明可以为空的字段。如果我设置IsNullable=false,我会得到异常“对于Nullable类型,IsNullable可能不会设置为'false'。”对于Nullable类型,IsNullable可能不会设置为'false'。考虑使用'System.Int32'类型或从XmlElement属性中删除IsNullable属性。“但如果我理解正确,设置IsNullable=true(或保留属性)隐式将元素设置为nillable,从而改变了架构。

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

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

(我知道在使用数据模型中的XxxSpecified属性序列化到xml时我可以省略nil-elements,但是据我所知,这种方法仍然需要添加nsble到xsd模式。)

修改:感谢您的评论,我现在更好地理解了这个问题。实际上有两个不同的问题:

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

  2. XmlSerializer需要数据模型中的可空类型 [XmlElement IsNullable=true],这意味着模型隐式添加 'nillable'到架构。我可以避免这个吗?

2 个答案:

答案 0 :(得分:5)

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

  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?);
            }
        }
    }
    
  6. 这很简单,您不必编写自己的序列化程序,也不必破解xsd或其他东西。

答案 1 :(得分:2)

我不确定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,因为它是开源的。