具有依赖项的XmlSerializer导致Null引用异常

时间:2013-07-15 21:07:17

标签: c# xml-serialization xml-deserialization

我遇到了我自己的类的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

有什么方法可以解决这个问题吗?

3 个答案:

答案 0 :(得分:2)

这里似乎很多问题源于使用您的域模型进行序列化。现在,这个可以工作,但如果您的域模型与序列化程序想要做的事情稍微偏离甚至,那么它也会产生很大的问题。

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

public double SomeValue { get; set; }

等。关键点在于它很简单并代表数据,而不是系统的规则。您可以序列化到此模型/从该模型序列化 - 这不应该很简单 - 并且您可以将其映射到域模型或从域模型映射。转换运算符可能很有用,但简单的“ToDomainModel”/“FromDomainModel”方法也可以正常工作。同样,像AutoMapper这样的工具可能会有所帮助,但15行DTO到/来自域代码也不会有任何损害。

这避免了以下问题:

  • 构造
  • 非公开会员
  • 作业顺序
  • 只读成员
  • 版本

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

答案 1 :(得分:0)

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

[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);

答案 2 :(得分:0)

Value getter和setter的一个问题是,如果在反序列化Value时没有加载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;
         }
    }