Your remaining problem is that you are trying to deserialize a string containing a pair of numbers to a System.Windows.Point
when the numbers have been formatted in a locale using a comma for a decimal separator. You will need to create a custom JsonConverter
for this situation:
public class PointConverter : JsonConverter
{
readonly NumberFormatInfo numberFormatInfo = new NumberFormatInfo
{
NumberDecimalSeparator = ",",
NumberGroupSeparator = ".",
};
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Point) || objectType == typeof(Point?);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
switch (reader.MoveToContentAndAssert().TokenType)
{
case JsonToken.Null:
return null;
case JsonToken.String:
{
var s = (string)reader.Value;
var values = s.Split(';');
if (values.Length != 2)
throw new JsonSerializationException(string.Format("Invalid Point format {0}", s));
try
{
return new Point(double.Parse(values[0], numberFormatInfo), double.Parse(values[1], numberFormatInfo));
}
catch (Exception ex)
{
throw new JsonSerializationException(string.Format("Invalid Point format {0}", s), ex);
}
}
default:
throw new JsonSerializationException(string.Format("Unexpected token {0}", reader.TokenType));
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(((Point)value).ToString(numberFormatInfo));
}
}
public static partial class JsonExtensions
{
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (reader.TokenType == JsonToken.None) // Skip past beginning of stream.
reader.ReadAndAssert();
while (reader.TokenType == JsonToken.Comment) // Skip past comments.
reader.ReadAndAssert();
return reader;
}
public static JsonReader ReadAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (!reader.Read())
throw new JsonReaderException("Unexpected end of JSON stream.");
return reader;
}
}
Then deserialize as follows:
var settings = new JsonSerializerSettings
{
Converters = { new PointConverter() },
};
var root = JsonConvert.DeserializeObject<TRootObject>(jsonString, settings);
You need to use a JsonConverter
because, while Json.NET does support serializing objects as strings using their built-in TypeConverter
, there seems to be a bug with the PointConverter
used to convert Point
to and from a string representation. Specifically, this built-in converter is culture-dependent when converting to a string representation, but culture-ignorant when converting back to a Point
.
This can be seen in the reference source for PointConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
:
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value == null)
{
throw GetConvertFromException(value);
}
String source = value as string;
if (source != null)
{
return Point.Parse(source);
}
return base.ConvertFrom(context, culture, value);
}
Notice that culture
is not passed into Point.Parse()
? Conversely, PointConverter.ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
does make make use of the incoming culture when formatting. That inconsistency is a bug.
And to confirm the bug, if you try to round-trip a Point
in, say, the German culture, an exception will be thrown. The following code will throw, demonstrating the problem:
TypeDescriptor.GetConverter(typeof(Point)).ConvertFrom(null, CultureInfo.GetCultureInfo("de-DE"), TypeDescriptor.GetConverter(typeof(Point)).ConvertTo(null, CultureInfo.GetCultureInfo("de-DE"), new Point(-1.5, 3.4), typeof(string)))