Json.net LinearGradientBrush serialization

788 views Asked by At

I tried to serialize a LinearGradientBrush (System.Windows.Media.LinearGradientBrush) with Json.net but without success:

var lg = new LinearGradientBrush();
lg.StartPoint = new Point(2,3);
lg.EndPoint = new Point(3.1,0);
lg.GradientStops = new GradientStopCollection(new []{ new GradientStop(Colors.Red, 0),new GradientStop(Colors.White, 1)});

var json = JsonConvert.SerializeObject(lg);

This code will output a string containing "System.Windows.Media.LinearGradientBrush" that is obviously wrong.

I tried with xml serialization and it works as expected:

var sb = new StringBuilder();
var writer = XmlWriter.Create(sb, new XmlWriterSettings(){ Indent=true});
var ser = new XmlSerializer(lg.GetType(), new []{ typeof(System.Windows.Media.MatrixTransform)});
ser.Serialize(writer,lg);
var xml = sb.ToString();

Why the json serializer fails to serialize a LinearGradientBrush?

2

There are 2 answers

0
Alberto Solano On

The library Json.NET has some problems while trying to serialize complex objects such as an array, a LinearGradientBrush object or an object with an advanced structure, like a Dictionary or something like that (in the case of a LinearGradientBrush, there's a GradientStopCollection data structure that could be the cause of your problem).

Try to serialize your object, in a more controlled way, as suggested in the documentation.

If this doesn't help, maybe it's the case to create a custom converter for your object, as suggested here, and use it while invoking the method SerializeObject() for the serialization of your object.

1
Robert On

Old question but still relevant with the latest Json.NET.

You can opt to only serialize the brush as XAML. This means you'll end up with XAML in your JSON file but it's the cleanest solution I could come up with.

Above your brush, use the JsonConvert annotation:

[JsonConverter(typeof(BrushJsonConverter))]
public Brush Brush {get; set;}

And implement the following JsonConverter:

/// <summary>
///     Stores a brush as XAML because Json.net has trouble saving it as JSON
/// </summary>
public class BrushJsonConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var jo = new JObject {{"value", XamlWriter.Save(value)}};
        jo.WriteTo(writer);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
        JsonSerializer serializer)
    {
        // Load JObject from stream
        var jObject = JObject.Load(reader);
        return XamlReader.Parse(jObject["value"].ToString());
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(Brush).IsAssignableFrom(objectType);
    }
}

EDIT: If you prefer to have pure JSON, you can also serialize the XAML to JSON. This may degrade performance but it does make for a tidier result

/// <summary>
///     Stores a brush by temporarily serializing it to XAML because Json.NET has trouble 
///     saving it as JSON
/// </summary>
public class BrushJsonConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // Turn the brush into an XML node
        var doc = new XmlDocument();
        doc.LoadXml(XamlWriter.Save(value));

        // Serialize the XML node as JSON
        var jo = JObject.Parse(JsonConvert.SerializeXmlNode(doc.DocumentElement));
        jo.WriteTo(writer);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
        JsonSerializer serializer)
    {
        // Load JObject from stream
        var jObject = JObject.Load(reader);

        // Seriaze the JSON node to XML
        var xml = JsonConvert.DeserializeXmlNode(jObject.ToString());
        return XamlReader.Parse(xml.InnerXml);
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(Brush).IsAssignableFrom(objectType);
    }
}