【问题标题】:How to deserialize null array to null in c#?如何在c#中将空数组反序列化为空?
【发布时间】:2011-02-02 09:48:23
【问题描述】:

这是我的课:

public class Command
{
   [XmlArray(IsNullable = true)]
   public List<Parameter> To { get; set; }
}

当我序列化这个类的一个对象时:

var s = new XmlSerializer(typeof(Command));
s.Serialize(Console.Out, new Command());

按预期打印(省略 xml 标头和默认 MS 命名空间):

<Command><To xsi:nil="true" /></Command>

当我使用这个 xml 并试图反序列化它时,我被卡住了,因为它总是打印“Not null”:

var t = s.Deserialize(...);
if (t.To == null)
    Console.WriteLine("Null");
else
    Console.WriteLine("Not null");

如果在 xml 中为空,如何强制反序列化器使我的列表为空?

【问题讨论】:

  • 没有清单或空清单对你来说真的那么重要吗?
  • 是的。我当然可以用一些标志来解决这种情况,但我想知道是否有一个好的标准解决方案。
  • 避免空对象通常是一件好事(不易出错,无需检查代码周围的空值等)。使用标志将清楚地表明该空值究竟代表什么。
  • 是的,但实际上,检查 null 将被检查标志所取代。谢谢你的建议;)

标签: c# xml arrays serialization nullable


【解决方案1】:

如果您使用数组而不是列表,它会按预期工作

public class Command
{
    [XmlArray(IsNullable = true)]
    public Parameter[] To { get; set; }
}

【讨论】:

    【解决方案2】:

    呃,是不是很烦人。您可以通过使用 /keep 和 /debug 选项在程序集上运行 sgen.exe 来查看它正在执行的操作,以便您可以调试反序列化代码。大致是这样的:

    global::Command o;
    o = new global::Command();
    if ((object)(o.@To) == null) o.@To = new global::System.Collections.Generic.List<global::Parameter>();
    global::System.Collections.Generic.List<global::Parameter> a_0 = (global::System.Collections.Generic.List<global::Parameter>)o.@To;
    // code elided
    //...
    while (Reader.NodeType != System.Xml.XmlNodeType.EndElement && Reader.NodeType != System.Xml.XmlNodeType.None) {
      if (Reader.NodeType == System.Xml.XmlNodeType.Element) {
        if (((object)Reader.LocalName == (object)id4_To && (object)Reader.NamespaceURI == (object)id2_Item)) {
          if (!ReadNull()) {
            if ((object)(o.@To) == null) o.@To = new global::System.Collections.Generic.List<global::Parameter>();
            global::System.Collections.Generic.List<global::Parameter> a_0_0 = (global::System.Collections.Generic.List<global::Parameter>)o.@To;
            // code elided
            //...
          }
          else {
            // Problem here:
            if ((object)(o.@To) == null) o.@To = new global::System.Collections.Generic.List<global::Parameter>();
            global::System.Collections.Generic.List<global::Parameter> a_0_0 = (global::System.Collections.Generic.List<global::Parameter>)o.@To;
          }
        }
      }
      Reader.MoveToContent();
      CheckReaderCount(ref whileIterations1, ref readerCount1);
    }
    ReadEndElement();
    return o;
    

    确保@To 属性不为空的不少于3 个位置。第一个有点防御性,当结构不存在时很难反序列化数据。第二个做空测试再次,这是唯一真正好的。第三个是问题,ReadNull() 返回 true 但它仍然创建了一个非空属性值。

    如果您想区分空和空,那么您没有好的解决方案,只能手动编辑此代码。 在你真的很绝望并且课程 100% 稳定时才这样做。好吧,不要这样做。 João 的解决方案是唯一好的解决方案。

    【讨论】:

      【解决方案3】:

      我同意@Oliver 的评论,但是如果你绝对需要它返回 null,你可以这样解决它。与其使用自动属性,不如创建您自己的支持字段。

      List<Parameter> _to;
      public List<Parameter> To
      {
          get
          {
              if (_to != null && _to.Count == 0) return null;
              return _to;
          }
          set { _to = value; }
      }
      

      【讨论】:

      • 我相信 OP 想要区分空列表和空列表。
      • 不幸的是,在我的情况下,空列表和空列表是不同的。
      【解决方案4】:

      如果您确实需要在未提供任何值时将集合反序列化为 null,您可以通过不提供 set 访问器来实现,如下所示:

      public class Command
      {
          private List<Parameter> to;
      
          public List<Parameter> To { get { return this.to; } }
      }
      

      【讨论】:

      • 错误的选择。 “To”在反序列化后将始终为空。
      【解决方案5】:

      对于那些需要它的人,您可以将类型定义为具有原始元素名称的数组,然后将其包装,这将获得可为空的列表。

      [XmlArray(ElementName = nameof(Metadata), IsNullable = true)]
      public string[] MetadataArray { get; set; }
      
      [XmlIgnore]
      public List<string> Metadata
      {
          get => this.MetadataArray?.ToList();
          set => this.MetadataArray = value?.ToArray();
      }
      

      【讨论】:

        猜你喜欢
        • 2022-01-11
        • 1970-01-01
        • 2022-01-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多