35

I got a scenario.

Required input and output are JSON.

// Input
{
  "OldObject": {
    "Time": 1351160457922,
    "Name": "OName",
    "quantity": 100,
    "price": 10
  }
}


// Output
{
  "NewObject": {
    "Time": 1351160457922,
    "Title": "OName",
    "quantity": 100
  }
}

I need some transformation code or preferably xslt type language to transform json from one format to another. This transformer also need to be fast as transformation will be done on the fly.

Edit
I don't have the definition of the INPUT object received and it might change at run time. but I can use class for OUTPUT object if needed. I have tried to do this as json -> xml -> xslt -> xml -> json, but approximately 1000 objects are received per second at this end and this process might incur overhead.
I can not also use JavaScript as myApp is simple windows based java application and using JavaScript might cause overhead.

Chaturvedi Dewashish
  • 1,469
  • 2
  • 15
  • 39

8 Answers8

37

Try JOLT. It is a JSON to JSON transformation library written in Java. It was created on a project that was transforming lot of JSON from an ElasticSearch "backend" to a frontend api.

For the JSON transform you have listed in your problem, the Jolt "shift" spec would be :

// Jolt "shift" spec
{
    "OldObject": {
        "Time": "NewObject.Time",   
        "Name": "NewObject.Title", // if the input has "OldObject.Name", copy it's value
                                   // to "NewObject.Title
        "quantity": "NewObject.quantity"
    }
}
Milo S
  • 4,466
  • 1
  • 19
  • 22
14

You can do this transformation with JSON patch.

Example with jsonpatch-js:

var transformations = [
  { move: '/OldObject', to: '/NewObject' },
  { remove: '/NewObject/price' },
  { move: '/NewObject/Name', to: '/NewObject/Title' }
];

var oldObject = { "OldObject": { "Time": 1351160457922, "Name": "OName", "quantity": 100, "price": 10 } };

jsonpatch.apply(oldObject, transformations);

I did not test the provided, but should work like that.

There are Java implementations for JSON patch:

KARASZI István
  • 30,900
  • 8
  • 101
  • 128
6

You can use ZORBA and JsonIQ http://www.jsoniq.org/ However, it's a native library, it comes with a wrapper so you can use it in java.

mvallebr
  • 2,388
  • 21
  • 36
  • Take a look at my wrapper too later, might be helpful: https://github.com/mvallebr/ZorbaJavaWrapper – mvallebr Nov 22 '12 at 20:19
2

You can try jmom a little java library

String jsonstring = "...";
JsonValue json = JsonParser.parse(jsonstring);
Jmom jmom = Jmom.instance()
            .copy("/OldObject", "/NewObject", true)
            .remove("/NewObject/price")
            .copy("/NewObject/Name", "/NewObject/Title", true);
jmom.apply(json);
jsonstring = json.toCompactString();
Anton Straka
  • 139
  • 2
1

Javascript JSON Transformer : https://raw.githubusercontent.com/udhayasoftware/codebase/master/standalone/javascript/TransformJSON.js

We can convert JSON array to JSON object and vice versa. The only thing is we need to be careful in defining the xPaths.

//Transforming JSON array to JSON object:
 var inputObj = [{Name:"Senyora"},{Name:"Clinton"}]
 sourceXpath = "[].Name";
 targetXpath = "Marriage.Couples[].NewName";
 // Output = {Marriage:{Couples:[{NewName:"Senyora"},{NewName:"Clinton"}]}}


 //Transforming JSON object to JSON array:
 var inputObj = {Marriage:{Couples:[{NewName:"Senyora"},{NewName:"Clinton"}]}}
 sourceXpath = "Marriage.Couples[].NewName";
 targetXpath = "[].Name";
 // Output = [{Name:"Senyora"},{Name:"Clinton"}]

/*

 Author: Udhayamoorthy
 Email: udhayaraagam@gmail.com"

 */

//Code start

function prepareGroup(inputObj, flatted, sourceXpath) {
    sourceXpath = sourceXpath.replace(/\[]/g, ".[0-9]*");
    var reg = new RegExp(sourceXpath, "g")
    var strVal = JSON.stringify(flatted).match(reg);
    var groupVal = {};
    if (strVal != null)
        strVal.forEach(function (data) {
            if (flatted[data] != undefined) {
                groupVal[data] = flatted[data];
            } else {
                data = data.replace(/"/g, "");
                groupVal[data] = getValue(inputObj, data);
            }
        })
    return groupVal;
}

function processGrouped(obj, targetXpath) {
    var flatOutput = {};
    var keys = Object.keys(obj);
    targetXpath = targetXpath.replace(/\[]./g, "[0-9]");
    for (var i = 0; i < keys.length; i++) {
        var key = keys[i];
        var changed = key.match(/(^[0-9]*\.|\W[0-9]*\.)/g);
        if (changed) {
            changed = JSON.stringify(changed).replace(/\"\./g, "\"");
        }
        var arrapos = '';
        try {
            arrapos = JSON.parse(changed);
        }
        catch (e) {
            arrapos = changed;
        }
        var temp = targetXpath;
        if (arrapos != null) {
            arrapos.forEach(function (pos) {
                pos = "." + pos;
                temp = temp.replace("[0-9]", pos)
            })
        }
        //tinkering - started
        if (temp.charAt(0) == ".") {
            temp = temp.substring(1, temp.length);
        }
        //tinkering - end
        flatOutput[temp] = obj[key];
    }
    return unflatten(flatOutput);
}

function merge(a, b) {
    for (var key in b)
        if (b.hasOwnProperty(key)) {
            var src = a[key];
            var dest = b[key];
            if (typeof src === 'object' && typeof dest === 'object') {
                merge(src, dest);
            } else {
                a[key] = b[key];
            }
        }
    return a;
};

function getValue(localObj, xpath) {
    //var localObj = JSON.parse(JSON.stringify(obj));
    var xpathArr = xpath.split('.');
    xpathArr.forEach(function (path) {
        localObj = localObj[path];
    })
    return localObj;
}

function unflatten(target, opts) {
    var opts = opts || {}
        , delimiter = opts.delimiter || '.'
        , result = {}

    if (Object.prototype.toString.call(target) !== '[object Object]') {
        return target
    }

    function getkey(key) {
        var parsedKey = parseInt(key)
        return (isNaN(parsedKey) ? key : parsedKey)
    };

    Object.keys(target).forEach(function (key) {
        var split = key.split(delimiter)
            , firstNibble
            , secondNibble
            , recipient = result

        firstNibble = getkey(split.shift())
        secondNibble = getkey(split[0])

        while (secondNibble !== undefined) {
            if (recipient[firstNibble] === undefined) {
                recipient[firstNibble] = ((typeof secondNibble === 'number') ? [] : {})
            }

            recipient = recipient[firstNibble]
            if (split.length > 0) {
                firstNibble = getkey(split.shift())
                secondNibble = getkey(split[0])
            }
        }

        // unflatten again for 'messy objects'
        recipient[firstNibble] = unflatten(target[key])
    });

    //Array Check
    var keys = Object.keys(result);
    if (keys.length > 0 && keys[0] === "0") {
        var output = [];
        keys.forEach(function (key) {
            output.push(result[key])
        });
        return output;
    }
    return result
};

function flatten(target, opts) {
    var output = {}
        , opts = opts || {}
        , delimiter = opts.delimiter || '.'

    function getkey(key, prev) {
        return prev ? prev + delimiter + key : key
    };

    function step(object, prev) {
        Object.keys(object).forEach(function (key) {
            var isarray = opts.safe && Array.isArray(object[key])
                , type = Object.prototype.toString.call(object[key])
                , isobject = (type === "[object Object]" || type === "[object Array]")

            if (!isarray && isobject) {
                return step(object[key]
                    , getkey(key, prev)
                )
            }

            output[getkey(key, prev)] = object[key]
        });
        if (Object.keys(object) == "") {
            if (object instanceof Array) {
                output[prev] = [];
            } else {
                output[prev] = {};
            }
        }
    };
    step(target)
    return output
};

function isChildAttribute(map, flatted, mapArray) {
    var parent = map.sourceXpath;
    for (var j = 0; j < mapArray.length; j++) {
        var child = mapArray[j].sourceXpath;
        if (child.indexOf(parent) != -1 && parent.length < child.length) {
            if (child.indexOf(parent + ".") != -1 || child.indexOf(parent + "[]") != -1) {
                var temp = child;
                temp = temp.replace(/\[]/g, ".0");
                if (flatted[temp] != undefined) {
                    return false;
                }
            }
        }
    }
    return true;
}

function transformJSON(inputObj, mapArray) {
    var flatted = flatten(inputObj);
    var finalout = {};
    if (mapArray.length > 0 && (mapArray[0].targetXpath).charAt(0) == "[")
        finalout = [];
    mapArray.forEach(function (map) {
        if (isChildAttribute(map, flatted, mapArray)) {
            var grouped = prepareGroup(inputObj, flatted, map.sourceXpath);
            var output = processGrouped(grouped, map.targetXpath);
            finalout = merge(finalout, output);  // merge two json objects
        }
    });
    return finalout;
}

//Code end

//How to use (See below) ??

var inputObj = {
    a: {
        b: [
            {
                Name: "Tommy",
                Location: [
                    {Place: "Sydney"},
                    {Place: "Washington"}
                ],
                Info: {age: 23}
            },
            {
                Name: "Sara",
                Location: [
                    {Place: "New York"},
                    {Place: "New Jercy"}
                ],
                Info: {age: 34}
            },
            {
                Name: "John",
                Location: [
                    {Place: "Chicago"},
                    {Place: "Detroit"}
                ],
                Info: {age: 78}
            }
        ],
        d: {
            e: {
                f: {
                    g: {
                        h: "I Love India"
                    }
                }
            }
        }
    }
};

var mapArray = [];     // collect source and target xpath s
var obj = {};
obj.sourceXpath = "a.b[].Name"; // Name is string
obj.targetXpath = "x[].NewName"; // expecting NewName as string
mapArray.push(obj);

//obj = {};
//obj.sourceXpath = "a.b[].Location"; // Location is an array
//obj.targetXpath = "x[].NewName"; // INVALID MAPPING - NewName already mapped
//mapArray.push(obj);

obj = {};
obj.sourceXpath = "a.b[].Location"; // Location is an array
obj.targetXpath = "x[].NewLocation"; // Location data copied to NewLocation array(Place will be present in array elements)
mapArray.push(obj);

obj = {};
obj.sourceXpath = "a.b[].Location[].Place"; // Location is an array
obj.targetXpath = "x[].NewLocation[].NewPlace"; // NewPlace will be created parallel to existing Place.
mapArray.push(obj);

obj = {};
obj.sourceXpath = "a.d.e.f.g.h"; // Transforming attributes at different level
obj.targetXpath = "T.H";
mapArray.push(obj);

var finalout = transformJSON(inputObj, mapArray);
console.log("See line#204 for more about how to use?");
console.log("Transformed JSON = " + JSON.stringify(finalout));

Caution: JSON can't be transformed between different dimensions of array. Count of '[]' in sourceXpath should be equals to count of '[]' in targetXpath and vice versa.

Nadhas
  • 5,421
  • 2
  • 28
  • 42
1

You can try Java library Silencio that allows you to convert each node of the JSON file into new values. You can decide which and how nodes should be transformed.

Damian
  • 437
  • 5
  • 11
1

Another option is use Logz.io Sawmill library. You define a pipeline and execute it. For you example:

{
  steps: [
    {
      rename {
         config {
            from: "OldObject"
            to: "NewObject"
         }
      }
    }

    {
      removeField {
        config {
          path: "NewObject.price"
        }
      }
    }

    {
      rename {
        config {
          from: "NewObject.Name"
          to: "NewObject.Title"
        }
      }
    }
  ]
}
Asaf Mesika
  • 1,643
  • 4
  • 20
  • 33
0

Another option is Josson. The transformation statement is very short:

map(NewObject: OldObject.map(Time,Title:Name,quantity))
Raymond Choi
  • 1,065
  • 2
  • 7
  • 8