序列化字典使用NodaTime.Instance到json使用json.net 工作正常,但在反序列化时它会抛出 Newtonsoft.Json。的 JsonSerializationException 即可。 下面的测试显示了问题:
[Test]
public void DeserializeDictionaryThowsException() {
JsonConverter[] converters = { NodaConverters.IntervalConverter, NodaConverters.InstantConverter };
var dictionary = new Dictionary<Instant, int>() {
{Instant.FromUtc(2012, 1, 2, 3, 4, 5), 0}
};
var json = JsonConvert.SerializeObject(dictionary, Formatting.None, converters);
Assert.AreEqual("{\"2012-01-02T03:04:05Z\":0}", json); //ok
var result = JsonConvert.DeserializeObject<Dictionary<Instant, int>>(json, converters); // throws
}
DeserializeObject抛出:
Newtonsoft.Json.JsonSerializationException:无法将字符串'2012-01-02T03:04:05Z'转换为字典键类型'NodaTime.Instant'。创建TypeConverter以将字符串转换为键类型对象。第1行,第24位。 ----&GT; Newtonsoft.Json.JsonSerializationException:将值“2012-01-02T03:04:05Z”转换为'NodaTime.Instant'时出错。第1行,第24位。 ----&GT; System.Exception:无法从System.String转换或转换为NodaTime.Instant。
作为旁注,反序列化DateTime字典可以正常工作。我猜因为String有一个DateTime的转换器。
[Test]
public void DeserializeDiciotnaryOfDateTime() // OK
{
var expected = new DateTime(2012, 1, 2, 3, 4, 5, DateTimeKind.Utc);
var dictionary = new Dictionary<DateTime, int>() { { expected, 0 } };
var json = JsonConvert.SerializeObject(dictionary);
var result = JsonConvert.DeserializeObject<Dictionary<DateTime, int>>(json);
Assert.AreEqual(expected, dictionary.Keys.First()); // OK
}
答案 0 :(得分:0)
您需要添加更多JSON.NET转换器来序列化NodaTime.Instance时间,如下所示。
public void DeserializeDictionaryThowsException()
{
var dtzProvider = DateTimeZoneCache.GetSystemDefault();
JsonConverter[] converters = { NodaConverters.IntervalConverter,
NodaConverters.InstantConverter,
NodaConverters.LocalDateConverter,
NodaConverters.LocalDateTimeConverter,
NodaConverters.LocalTimeConverter,
NodaConverters.OffsetConverter,
NodaConverters.DurationConverter,
NodaConverters.RoundtripPeriodConverter,
NodaConverters.OffsetDateTimeConverter,
NodaConverters.CreateDateTimeZoneConverter(dtzProvider),
NodaConverters.CreateZonedDateTimeConverter(dtzProvider)
};
var dictionary = new Dictionary<Instant, int>() { { Instant.FromUtc(2012, 1, 2, 3, 4, 5), 0 } };
var json = JsonConvert.SerializeObject(dictionary, Formatting.None, converters);
Assert.AreEqual("{\"2012-01-02T03:04:05Z\":0}", json);
var result = JsonConvert.DeserializeObject<Dictionary<Instant, int>>(json, converters);
}
答案 1 :(得分:0)
此问题正在https://github.com/nodatime/nodatime.serialization/issues/2
处理我的工作是从http://stackoverflow.com/q/6845364/634824修改过来的。 这绝不是高性能,也没有经过全面测试。
public class DictionaryWithNodaTimeKeyConverter : JsonConverter
{
private static IDateTimeZoneProvider dtzProvider = DateTimeZoneProviders.Tzdb;
private JsonSerializerSettings _settings;
public DictionaryWithNodaTimeKeyConverter(IDateTimeZoneProvider dtzProvider)
: base()
{
_settings = new JsonSerializerSettings().ConfigureForNodaTime(dtzProvider);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
IDictionary dictionary = (IDictionary)value;
writer.WriteStartObject();
foreach (object key in dictionary.Keys)
{
writer.WritePropertyName(ConvertToPropertyKey(key));
serializer.Serialize(writer, dictionary[key]);
}
writer.WriteEndObject();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
Type keyType = objectType.GetGenericArguments()[0];
Type valueType = objectType.GetGenericArguments()[1];
Type intermediateDictionaryType = typeof(Dictionary<,>).MakeGenericType(typeof(string), valueType);
IDictionary intermediateDictionary = (IDictionary)Activator.CreateInstance(intermediateDictionaryType);
serializer.Populate(reader, intermediateDictionary);
IDictionary finalDictionary = (IDictionary)Activator.CreateInstance(objectType);
foreach (DictionaryEntry pair in intermediateDictionary)
{
object parsedObject;
if (TryConvertKey(pair.Key.ToString(), keyType, out parsedObject))
{
finalDictionary.Add(parsedObject, pair.Value);
}
}
return finalDictionary;
}
public override bool CanConvert(Type objectType)
{
bool canConvert = objectType.IsA(typeof(IDictionary<,>));
if (canConvert)
{
Type keyType = objectType.GetGenericArguments()[0];
canConvert = canConvert && IsNodaTimeType(keyType);
}
return canConvert;
}
private bool IsNodaTimeType(Type type)
{
return type.IsA(typeof(Instant))
|| type.IsA(typeof(OffsetDateTime))
|| type.IsA(typeof(DateTimeZone))
|| type.IsA(typeof(ZonedDateTime))
|| type.IsA(typeof(LocalDateTime))
|| type.IsA(typeof(LocalDate))
|| type.IsA(typeof(LocalTime))
|| type.IsA(typeof(Offset))
|| type.IsA(typeof(Duration))
|| type.IsA(typeof(Period));
// Interval is not Support because Interval is serialized as a compound object.
}
private string ConvertToPropertyKey(object property)
{
if (!IsNodaTimeType(property.GetType()))
{
throw new InvalidOperationException();
}
string result = JsonConvert.SerializeObject(property, _settings);
if (!string.IsNullOrWhiteSpace(result))
{
// Remove the "" from JsonConvert
int first = result.IndexOf('"');
int last = result.LastIndexOf('"');
if (first != -1 && last != -1 && first < last)
{
result = result.Substring(first + 1, last - (first + 1));
}
}
return result;
}
private bool TryConvertKey(string text, Type keyType, out object value)
{
if (!IsNodaTimeType(keyType))
{
throw new InvalidOperationException();
}
value = keyType.CreateDefault();
try
{
value = JsonConvert.DeserializeObject($"\"{text}\"", keyType, _settings);
return true;
}
catch
{
return false;
}
}
}
我还定义了一些扩展
public static class TypeExtensions
{
public static bool IsA(this Type type, Type typeToBe)
{
if (!typeToBe.IsGenericTypeDefinition)
return typeToBe.IsAssignableFrom(type);
List<Type> toCheckTypes = new List<Type> { type };
if (typeToBe.IsInterface)
toCheckTypes.AddRange(type.GetInterfaces());
Type basedOn = type;
while (basedOn.BaseType != null)
{
toCheckTypes.Add(basedOn.BaseType);
basedOn = basedOn.BaseType;
}
return toCheckTypes.Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeToBe);
}
public static object CreateDefault(this Type type)
{
return type.IsValueType ? Activator.CreateInstance(type) : null;
}
}
使用它:
IDateTimeZoneProvider provider = DateTimeZoneProviders.Tzdb;
JsonConverter[] converters = { NodaConverters.IntervalConverter, NodaConverters.InstantConverter, new DictionaryWithNodaTimeKeyConverter(provider) };
var dictionary = new Dictionary<Instant, int> {
{ Instant.FromUtc(2012, 1, 2, 3, 4, 5), 0 }
};
var json = JsonConvert.SerializeObject(dictionary, Formatting.None, converters);
Console.WriteLine(json);
var result = JsonConvert.DeserializeObject<Dictionary<Instant, int>>(json, converters);