问题
I'm running into an issue with Xml serialization of my own class. It is a derived class, which doesn't naturally have a parameterless constructor - I had to add one just for the sake of serialization. Of course, because of that I'm running into dependency/order issue.
Here's a simplification, which I hope still illustrates the problem (I reserve the right to augment the illustration if it turns out I didn't capture the problem - I just didn't want to dump a complicated Object Model on you :))
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; }
}
}
EDIT: to avoid confusion, I substituted the value type double
in the original post with a not-shown-here Vector
type
When XmlSerializer de-serializes Derived
, I run into null value exception - Both base.Value
and this.Coefficient
are null
.
Is there any way to fix this?
回答1:
It seems that a lot of the issues here stem from using your domain model for serialization. Now, this can work, but it can also be hugely problematic if your domain model deviates even slightly from what the serializer wants to do.
I strongly suggest trying to add a second parallel representation of the data, as a "DTO model" - meaning: a set of objects whose job is to represent the data for serialization. S instead of a complicated property with calculations and dependencies, you just have:
public double SomeValue { get; set; }
etc. The key point is that is is simple and represents the data, not your system's rules. You serialize to/from this model - which should not be simple - and you map this to/from your domain model. Conversion operators can be useful, but a simple "ToDomainModel" / "FromDomainModel" method works fine too. Likewise, tools like AutoMapper might help, but 15 lines of DTO-to/from-Domain code isn't going to hurt either.
This avoids issues with:
- constructors
- non-public members
- assignment order
- read-only members
- versioning
And a range of other common pain points in serialization.
回答2:
One problem with your Value getter and setter is that if Coefficient is not loaded at the time when de-serializing the Value, then it will cause divide by zero errors. Even worse, it might not break, but instead, actually do the calculation against an incorrect value since Coefficient may have a pre-deserialization value stored in it. The following will solve the divide by zero situation, and hopefully update value correctly if coefficient loads second. In truth, these situations are usually handled better by serializing the non calculated value and then using [XmlIgnoreAttribute] on the derived property.
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;
}
}
回答3:
You need to tell the serializer that your base object has derived items. Try:
[XmlInclude(typeof(Derived))]
public class Base {
Alternatively, you can explain this at run time with:
public XmlSerializer(Type type, Type[] extraTypes){..}
In your case: new XmlSerializer(typeof(Base), new Type[] { typeof(Derived), ..});
And to make things even more generic, if there is a huge hierarchy, you can use reflection to get a list of the derived types:
// 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);
来源:https://stackoverflow.com/questions/17663948/xmlserializer-with-dependencies-results-in-null-reference-exception