问题
I'm trying unsuccessfully to create custom DateTime converter. The problem: I have many objects to serialize, some of the containing property of DateTime with DateTime.MinValue in it. I want to serialize it as null. But all the solution that I foud asking to decorate the proper inside the object (I can't do it) Other solution that I found below, is to create converter, As far as I can understand, this convertor works only DateTime object returned explicitly and not inside other object. Please help.
public class DateTimeConverter : JsonConverter
{
private readonly Type[] types;
public DateTimeConverter(params Type[] types)
{
this.types = types;
}
public override bool CanConvert(Type objectType)
{
return types.Any(t => t == objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
if (value is DateTime && value.Equals(DateTime.MinValue))
{
t = JToken.FromObject(null);
t.WriteTo(writer);
}
else
{
t.WriteTo(writer);
}
}
else
{
if (value.Equals(DateTime.MinValue)) {
t = JToken.FromObject(null);
t.WriteTo(writer);
}
else {
t.WriteTo(writer);
}
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
回答1:
I think you are overcomplicating things. Your converter should only worry about converting date values, not objects that may contain date values. The serializer will call your converter naturally whenever a date is encountered inside any other object.
Here is a simple converter that will do what you want:
public class MinDateToNullConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
// This converter handles date values directly
return (objectType == typeof(DateTime));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// The CanConvert method guarantees the value will be a DateTime
DateTime date = (DateTime)value;
if (date == DateTime.MinValue)
{
writer.WriteNull();
}
else
{
writer.WriteValue(date);
}
}
public override bool CanRead
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Here is a demo showing that the converter works on dates throughout a nested hierarchy of objects:
class Program
{
static void Main(string[] args)
{
Example example = new Example
{
Date1 = new DateTime(2014, 2, 2),
Date2 = DateTime.MinValue,
Inner = new Inner
{
DateA = DateTime.MinValue,
DateB = new DateTime(1954, 1, 26)
},
MoreDates = new List<DateTime>
{
new DateTime(1971, 11, 15),
DateTime.MinValue
}
};
// Set up the serializer to use our date converter
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new MinDateToNullConverter());
settings.Formatting = Formatting.Indented;
string json = JsonConvert.SerializeObject(example, settings);
Console.WriteLine(json);
}
}
class Example
{
public DateTime Date1 { get; set; }
public DateTime Date2 { get; set; }
public Inner Inner { get; set; }
public List<DateTime> MoreDates { get; set; }
}
class Inner
{
public DateTime DateA { get; set; }
public DateTime DateB { get; set; }
}
Output:
{
"Date1": "2014-02-02T00:00:00",
"Date2": null,
"Inner": {
"DateA": null,
"DateB": "1954-01-26T00:00:00"
},
"MoreDates": [
"1971-11-15T00:00:00",
null
]
}
回答2:
When converter implements CanConvert
(and yours does), you can add it to serializer settings:
JsonConvert.SerializeObject(foo, new JsonSerializerSettings {
Converters = {
new DateTimeConverter()
}
});
This way, it will be applied to all objects it supports.
回答3:
Found the answer to my question. Actually I had 2 converters in JsonSerializer settings. I noticed that when I removed the converter on object A which contains DateTime MinValue property, My serializer works fine. This led me understand that the converter of DateTime to null is not working under other converter. So what I did is converting manually inside object A converter, not exactly clean solution, and I would prefer that the DateTimeToNull Converter will also work inside A converter. Here is what I have done inside A converter:
public class AConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(A));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken jToken = JToken.FromObject(value);
JObject jObject = (JObject)jToken;
A aInfo = (A) value;
string statusString = aInfo.Status == null ? string.Empty : SomeUtil.GetStateString((State)aInfo.Status.State, aInfo.Status.Downloading);
jObject.AddFirst(new JProperty("MachineState", statusString));
if (aInfo.UploadTime == DateTime.MinValue) {
jObject.Remove("UploadTime");
jObject.Add(new JProperty("UploadTime", null));
}
jObject.WriteTo(writer);
}
public override bool CanRead
{
get
{
return false;
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("It's OK!");
}
}
来源:https://stackoverflow.com/questions/21508497/custom-datetime-serialization-with-json-net