JSON to JSON transformer
Asked Answered
Q

9

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.

Quamash answered 25/10, 2012 at 12:10 Comment(7)
yeah sure, i can use javascript. but more preferably xslt type language and transformer processorQuamash
What about JSON patch? tools.ietf.org/html/draft-pbryan-json-patch-00Autopilot
You can find implementations for it, for example: github.com/bruth/jsonpatch-jsAutopilot
A little concern - Will this will be good enough performance-wise as this Javascript will have to patch about 1000 JSON objects per seconds?Quamash
Then you should remove the javascript tag :)Autopilot
possible duplicate of XSLT equivalent for JSONSubway
I wrote a library for doing exactly this (in Groovy, easy to integrate in Java and simple enough to port): github.com/ripdajacker/json-transformerWinou
H
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"
    }
}
Haugh answered 1/7, 2013 at 20:34 Comment(0)
B
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:

Bobbysoxer answered 25/10, 2012 at 12:20 Comment(3)
A little concern - Will this will be good enough performance-wise as this Javascript will have to patch about 1000 JSON objects per seconds?Quamash
It depends on your situation. If you want to do that on the server side, you can have different implementations in different languages. I posted this because there was a javascript tag in your question.Autopilot
I am using java and it will be windows based desktop application.Quamash
J
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.

Jahvist answered 6/11, 2012 at 20:43 Comment(1)
Take a look at my wrapper too later, might be helpful: github.com/mvallebr/ZorbaJavaWrapperJahvist
F
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();
Fibered answered 20/11, 2019 at 18:21 Comment(0)
J
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: [email protected]"

 */

//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.

Japonica answered 5/1, 2015 at 13:1 Comment(0)
T
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.

Tipsy answered 18/10, 2015 at 18:26 Comment(0)
E
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"
        }
      }
    }
  ]
}
Emanuelemanuela answered 22/2, 2018 at 13:58 Comment(0)
E
0

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

map(NewObject: OldObject.map(Time,Title:Name,quantity))
Eec answered 15/9, 2022 at 15:44 Comment(0)
E
0

Use JSONata:

{
    "NewObject": {
        "Time": OldObject.Time,
        "Title": OldObject.Name,
        "quantity": OldObject.quantity
    }
}

See How can I use JSONata in Java? for approaches to using JSONata in Java.

Eluvium answered 23/1 at 14:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.