【问题标题】:XmlSerializer with dependencies results in Null reference exception具有依赖关系的 XmlSerializer 导致 Null 引用异常
【发布时间】:2013-07-15 21:07:17
【问题描述】:

我遇到了我自己的类的 Xml 序列化问题。它是一个派生类,它自然没有无参数的构造函数——我不得不添加一个只是为了序列化。当然,正因为如此,我遇到了依赖/顺序问题。

这是一个简化,我希望它仍然能说明问题(如果事实证明我没有捕捉到问题,我保留增加说明的权利 - 我只是不想将复杂的对象模型转储给你: ))

public class Base{
  public virtual Vector Value{ get; set;}
}

public class Derived : Base{

  public Vector Coefficient { get; set; }
  public override Vector Value{
     get { return base.Value * Coefficient; }
     set { base.Value = value / Coefficient; }
  }
}

编辑:为避免混淆,我将原帖中的值类型double 替换为此处未显示的Vector 类型

当 XmlSerializer 反序列化 Derived 时,我遇到空值异常 - base.Valuethis.Coefficient 都是 null

有没有办法解决这个问题?

【问题讨论】:

  • 那些是值类型,不可能是null。发布确切的异常信息。
  • @HenkHolterman:授予。这是一种简化——在实际情况下,这些不是值类型,它们是空的——我用调试器检查过。
  • 你试过[XmlInclude(typeof(Derived))]吗?
  • @terrybozzio - 实际上我有(没有完全理解它的作用)。它返回编译器时错误:属性“System.Xml.Serialization.XmlInclude”在此声明类型上无效。它仅对“类、结构、方法、接口”声明有效。
  • 说实话,根据访问/分配属性的顺序来正确使用只是一个设计错误。您可能可以使用 XmlElementAttribute 上的 Order 属性对其产生一些影响,但我的建议是:找到一个不依赖于属性访问/分配顺序的设计。

标签: c# xml-serialization xml-deserialization


【解决方案1】:

看来这里的很多问题都源于使用您的域模型进行序列化。现在,这个可以工作,但如果你的域模型稍微偏离序列化程序想要做的事情,它也会有很大的问题。

我强烈建议尝试添加数据的第二个并行表示,作为“DTO 模型” - 意思是:一组对象,其工作是表示数据以进行序列化。 S 而不是具有计算和依赖关系的复杂属性,您只需:

public double SomeValue { get; set; }

等等。关键是它很简单并且代表数据,而不是你系统的规则。你序列化到/从这个模型 - 这不应该是简单的 - 你将它映射到/从你的域模型。转换运算符可能很有用,但简单的“ToDomainModel”/“FromDomainModel”方法也可以正常工作。同样,AutoMapper 之类的工具可能会有所帮助,但 15 行 DTO-to/from-Domain 代码也无济于事。

这避免了以下问题:

  • 构造函数
  • 非公共成员
  • 分配顺序
  • 只读成员
  • 版本控制

以及一系列其他常见的序列化痛点。

【讨论】:

  • 我想我明白你在说什么。谢谢!
  • 还有一个问题。我将XmlIgnore 放在Derived.Value 上,但它仍被XmlSerializer 调用。我猜这是因为花瓶属性是虚拟的。 base.Value 本身实际上并不是“域模型”——它是准系统属性。有什么建议吗?
  • @NewDev 序列化程序将查看声明的值。显然问题会在 DTO 中消失,但您也可以使用 XmlAttributeOverrides - 注意这样做意味着您需要手动缓存和重用序列化程序实例。否则你会比你意识到的更快地泄漏内存。
【解决方案2】:

您的 Value getter 和 setter 的一个问题是,如果在反序列化 Value 时未加载 Coefficient,则会导致除以零错误。更糟糕的是,它可能不会中断,而是实际上针对不正确的值进行计算,因为 Coefficient 可能存储了预反序列化值。以下将解决除以零的情况,如果系数第二次加载,希望能正确更新值。事实上,通过序列化非计算值然后在派生属性上使用 [XmlIgnoreAttribute] 通常可以更好地处理这些情况。

public class Derived : Base{


    public override double Value{
       get { return _coefficient; }
       set { 
          if(Coefficient == 0){
          base.Value = value;
       }else{
           base.Value = value / Coefficient; 
       }
    }

   private double _coefficient;
   public double Coefficient{
       get { return _coefficient; }
       set { 
           if(Coefficient == 0)
           {
               temp = base.Value;
               _coefficient = value;
               Value = temp; 
           }
           else{
                _coefficient = value;
           }
    }

}


// Example by serializing unmodified value
public double Coefficient { get; set; }
public double BaseValue { get; set; }

[XmlIgnoreAttribute]
public double Value
{
   get { return BaseValue * Coefficient; }
   set 
   { 
         if(Coefficient != 0){
            BaseValue = value / Coefficient;
         }else{
            BaseValue = value;
         }
    }

【讨论】:

  • 不幸的是,这仅解决了我给出的简化示例...我的问题不是除以零,而是某些值在需要之前尚未实例化。
  • 主要是你应该序列化不依赖于其他值的简单表示,比如我的第二个例子,并忽略派生的属性,否则你可能会从序列化中遇到意想不到的问题,包括搞砸值。
  • 是的,看起来这也是 Marc 的建议。 +1
【解决方案3】:

您需要告诉序列化程序您的基础对象具有派生项。试试:

[XmlInclude(typeof(Derived))]
public class Base {

或者,您可以在运行时解释这一点:

public XmlSerializer(Type type, Type[] extraTypes){..}

在你的情况下:new XmlSerializer(typeof(Base), new Type[] { typeof(Derived), ..});

为了使事情更加通用,如果有一个巨大的层次结构,你可以使用反射来获取派生类型的列表:

// You'll want to cache this result, and it could be a lot of work to run this
// multiple times if you have lots of classes
var knownTypes = Assembly.GetExecutingAssembly().GetTypes().Where(
t => typeof(Base).IsAssignableFrom(t)).ToArray();
var serializer = new XmlSerializer(typeof(Base), knownTypes);

【讨论】:

  • 这使事情变得复杂,因为在实际情况下,我有多个级别的子类。事实上,我什至不知道如何声明它,因为 Base 是 Derived 类泛型,即 Derived : Base.
  • 啊,那么您将需要使用反射。我将更新上述内容,为您提供有关如何执行此操作的指南(这将使您不必使用 DTO 方法镜像您的结构)
  • 但在您的情况下,您在覆盖中有业务部门逻辑,这将导致 div by 0 问题等,具体取决于反序列化
猜你喜欢
  • 1970-01-01
  • 2018-10-03
  • 2016-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-05
  • 2014-04-12
  • 1970-01-01
相关资源
最近更新 更多