Deep Merging

Borislav Iordanov edited this page Jul 4, 2016 · 3 revisions

The Json.with method allows you to copy over the content of its argument into this element. Type must must: you can merge an object with an object or an array with an array.

By default, the method does the simplest thing - if the element is a JSON object, it does a copy of all properties and if it is an array, it just appends all the elements of its argument. So, if we have:

Json dest = object("name", "John");
Json src = object("phone", "212-454-3490", 
                  "address", object("street", "23 4th avenue", 
                                    "city", "New York"));
dest.with(src);

The dest variable above will now contain also the phone number and address of John. However, note that the copy is not a deep one. If one change the address via the src object, e.g. by doing src.at("address").set("city", "Manhattan"), the change will be reflected in dest as well. Also, both the phone and address values will now have the two parents src and dest. That is src.at("phone").up() will be equals to the array [src, dest].

Options

You can exercise control over the process via options. The with method takes a sequence of options that govern how the copying is done. Different options are pertinent to objects and to arrays. Moreover, you can apply different options at different paths within a JSON structure. Maybe too much flexibility, but it's there if you need it. Here are the options:

  • merge (for objects) - The properties of the source are recursively merged, instead of just copying the top-level properties. As a consequence, deeply nested properties in the destination object that do not appear in the source will be preserved.
  • dup (for objects and arrays) - The properties of the source are cloned. In particular this means, that the values won't end up with the destination object as a parent.
  • sort (for arrays) - Perform a merge sort on arrays. Note that this assumes that arrays are already being maintained in order! If the arrays contain objects, you can use the compareBy option to indicate which property the objects should be compared by.
  • compareBy (for arrays) - The name of the object property to compare array by during sort merge. The value of that property must be a Comparable (i.e. a number of a string).

Specifying Options

Options driving the with behavior can be specified at the top-level or at some nesting level. If you say:

dest.with(src, "dup")

That means the top-level object will be duplicated, and duplication/cloning is recursive anyway so you get a complete copy.

If instead you specify:

dest.with(src, "merge", object("for", ["address", "employer"], "dup", true))

It means that for the nested properties address and employer, the values will be duplicated. Note that the top-level "merge" option is specified as well, for otherwise, there won't be a recursive processing of the nested structures.

Support

It must be noted that the above behaviors are provided by the default implementation of Json.Factory and they are not mandated by the interface. They are also sort of experimental, which doesn't mean they are not supported or that they will be dropped - it just means, one must be careful when using them.