Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modify existing object with new partial JSON data using Json.NET

Consider the below example program

var calendar = new Calendar
{
    Id = 42,
    CoffeeProvider = "Espresso2000",
    Meetings = new[]
    {
        new Meeting
        {
            Location = "Room1",
            From = DateTimeOffset.Parse("2014-01-01T00:00:00Z"),
            To = DateTimeOffset.Parse("2014-01-01T01:00:00Z")
        },
        new Meeting
        {
            Location = "Room2",
            From = DateTimeOffset.Parse("2014-01-01T02:00:00Z"),
            To = DateTimeOffset.Parse("2014-01-01T03:00:00Z")
        },
    }
};

var patch = @"{
        'coffeeprovider': null,
        'meetings': [
            {
                'location': 'Room3',
                'from': '2014-01-01T04:00:00Z',
                'to': '2014-01-01T05:00:00Z'
            }
        ]
    }";

var patchedCalendar = Patch(calendar, patch);

The result of the Patch() method should be equal to calendar except as changed by patch. That means; Id would be unchanged, CoffeeProvider would be set to null and Meetings would contain a single item located in Room3.

  1. How does one create a general Patch() method that will work for any object (not just the example Calendar object) deserializable by Json.NET?

  2. If (1) this is not feasible, what would be some restrictions that would make it feasible and how would it be implemented?

like image 473
Linus Avatar asked Dec 16 '14 18:12

Linus


1 Answers

You want JsonSerializer.Populate() or its static wrapper method JsonConvert.PopulateObject():

Populates the JSON values onto the target object.

For instance, here it is updating an instance of your Calendar class:

public static class TestPopulate
{
    public static void Test()
    {
        var calendar = new Calendar
        {
            Id = 42,
            CoffeeProvider = "Espresso2000",
            Meetings = new[]
            {
                new Meeting
                {
                    Location = "Room1",
                    From = DateTimeOffset.Parse("2014-01-01T00:00:00Z"),
                    To = DateTimeOffset.Parse("2014-01-01T01:00:00Z")
                },
                new Meeting
                {
                    Location = "Room2",
                    From = DateTimeOffset.Parse("2014-01-01T02:00:00Z"),
                    To = DateTimeOffset.Parse("2014-01-01T03:00:00Z")
                },
            }
        };

        var patch = @"{
    'coffeeprovider': null,
    'meetings': [
        {
            'location': 'Room3',
            'from': '2014-01-01T04:00:00Z',
            'to': '2014-01-01T05:00:00Z'
        }
    ]
}";
        Patch(calendar, patch);

        Debug.WriteLine(JsonConvert.SerializeObject(calendar, Formatting.Indented));
    }

    public static void Patch<T>(T obj, string patch)
    {
        var serializer = new JsonSerializer();
        using (var reader = new StringReader(patch))
        {
            serializer.Populate(reader, obj);
        }
    }
}

And the debug output produced is:

{
  "id": 42,
  "coffeeprovider": null,
  "meetings": [
    {
      "location": "Room3",
      "from": "2014-01-01T04:00:00+00:00",
      "to": "2014-01-01T05:00:00+00:00"
    }
  ]
}

Update

If you want to copy first, you could do:

    public static T CopyPatch<T>(T obj, string patch)
    {
        var serializer = new JsonSerializer();

        var json = JsonConvert.SerializeObject(obj);
        var copy = JsonConvert.DeserializeObject<T>(json);

        using (var reader = new StringReader(patch))
        {
            serializer.Populate(reader, copy);
        }

        return copy;
    }
like image 192
dbc Avatar answered Sep 18 '22 16:09

dbc