Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Templated serialization of C# objects to JSON

Tags:

json

c#

.net

I need to serialize objects to JSON. I would like to do it with a template instead of using data annotations (as most frameworks do). Does anybody know a good way of doing this?

A picture says more than 1000 words. I'm looking for something that looks like this:

enter image description here

For example, if I had a class like this:

public class Test  
{ 
    public string Key { get; set; } 
    public string Name { get; set; } 
    public string Code { get; set; } 
    public Test Related { get; set; } 
} 

And a had template string that could look like this:

{ 
    id: "$Key",
    name: "$Name",
    related: "$Related.Name"
}

I want to get a JSON object, whose properties are filled in according to Key, Name and Related.Name of the object.

Basically I'm searching for a JSON serialization method that supports templating instead.

like image 221
Kees C. Bakker Avatar asked Feb 07 '12 10:02

Kees C. Bakker


People also ask

What is the purpose of serialization?

Serialization is the process of converting an object into a stream of bytes to store the object or transmit it to memory, a database, or a file. Its main purpose is to save the state of an object in order to be able to recreate it when needed. The reverse process is called deserialization.

How does boost serialization work?

Boost serialization works in two ways: one is called the intrusive method and another is the non-intrusive method. The intrusive method is the simplest one where we build mechanism of serialization in the object itself.

What is serialization in. net?

Serialization is the process of converting the state of an object into a form that can be persisted or transported. The complement of serialization is deserialization, which converts a stream into an object. Together, these processes allow data to be stored and transferred.


1 Answers

I don't know about any library that does this for you, but it's not that hard to build it yourself.

If you have your template, you need to parse it as JSON and then replace all of the placeholders with actual values. To do that, you can use the visitor pattern.

Since JSON.NET (the JSON library I'm using) doesn't seem to have a visitor, you can create one yourself:

abstract class JsonVisitor
{
    public virtual JToken Visit(JToken token)
    {
        var clone = token.DeepClone();
        return VisitInternal(clone);
    }

    protected virtual JToken VisitInternal(JToken token)
    {
        switch (token.Type)
        {
        case JTokenType.Object:
            return VisitObject((JObject)token);
        case JTokenType.Property:
            return VisitProperty((JProperty)token);
        case JTokenType.Array:
            return VisitArray((JArray)token);
        case JTokenType.String:
        case JTokenType.Integer:
        case JTokenType.Float:
        case JTokenType.Date:
        case JTokenType.Boolean:
        case JTokenType.Null:
            return VisitValue((JValue)token);
        default:
            throw new InvalidOperationException();
        }
    }

    protected virtual JToken VisitObject(JObject obj)
    {
        foreach (var property in obj.Properties())
            VisitInternal(property);

        return obj;
    }

    protected virtual JToken VisitProperty(JProperty property)
    {
        VisitInternal(property.Value);

        return property;
    }

    protected virtual JToken VisitArray(JArray array)
    {
        foreach (var item in array)
            VisitInternal(item);

        return array;
    }

    protected virtual JToken VisitValue(JValue value)
    {
        return value;
    }
}

And then create a specialized visitor that replaces the placeholders with actual values:

class JsonTemplateVisitor : JsonVisitor
{
    private readonly object m_data;

    private JsonTemplateVisitor(object data)
    {
        m_data = data;
    }

    public static JToken Serialize(object data, string templateString)
    {
        return Serialize(
            data, (JToken)JsonConvert.DeserializeObject(templateString));
    }

    public static JToken Serialize(object data, JToken template)
    {
        var visitor = new JsonTemplateVisitor(data);

        return visitor.Visit(template);
    }

    protected override JToken VisitValue(JValue value)
    {
        if (value.Type == JTokenType.String)
        {
            var s = (string)value.Value;

            if (s.StartsWith("$"))
            {
                string path = s.Substring(1);

                var newValue = GetValue(m_data, path);

                var newValueToken = new JValue(newValue);

                value.Replace(newValueToken);

                return newValueToken;
            }
        }

        return value;
    }

    private static object GetValue(object data, string path)
    {
        var parts = path.Split('.');

        foreach (var part in parts)
        {
            if (data == null)
                break;

            data = data.GetType()
                .GetProperty(part)
                .GetValue(data, null);
        }

        return data;
    }
}

The usage is then simple. For example, with the following template:

{ 
    id : "$Key",
    name: "$Name",
    additionalInfo:
    {
        related: [ "$Related.Name" ]
    }
}

You can use code like this:

JsonTemplateVisitor.Serialize(data, templateString)

The result then looks like this:

{
  "id": "someKey",
  "name": "Isaac",
  "additionalInfo": {
    "related": [
      "Arthur"
    ]
  }
}

You might want to add some error-checking, but other than that, the code should work. Also, it uses reflection, so it might not be suitable if performance is important.

like image 164
svick Avatar answered Sep 25 '22 12:09

svick