'JSONPath to transform and map to different tiers of data with a single output
I am using JSONPath and this node module https://www.npmjs.com/package/jsonpath-object-transform to transform a JSON obj into a different output. This is similar to XML and XSLT. The problem is when I want to map to different tiers of the source data and send to a flat output.
Examples:
var output = {
storeName: '$.store',
items: ['$.items',{
uniqueName: '$.name',
sku: '$.skus[*].num'
}]
};
var source = {
store:'my store',
items: [{
name: 'Hammer',
skus: [{
num: '12345qwert'
}]
}, {
name: 'Bike',
skus: [{
num: 'asdfghhj'
}, {
num: 'zxcvbn'
}]
}, {
name: 'Fork',
skus: [{
num: '0987dfgh'
}]
}]
};
The result of this is:
{
"storeName": "my store",
"items": [
{
"uniqueName": "Hammer",
"sku": "12345qwert"
},
{
"uniqueName": "Bike",
"sku": "asdfghhj"
},
{
"uniqueName": "Fork",
"sku": "0987dfgh"
}
]
}
The problem with the result is that it ignored the second array item in the Bike
obj. items[1].skus[1].num
. I believe it did this because $.items
designator only knows of 3 obj in the items array.
My expected result was :
{
"storeName": "my store",
"items": [
{
"uniqueName": "Hammer",
"sku": "12345qwert"
},
{
"uniqueName": "Bike",
"sku": "asdfghhj"
},
{
"uniqueName": "Bike",
"sku": "zxcvbn"
},
{
"uniqueName": "Fork",
"sku": "0987dfgh"
}
]
}
How can I achieve this output using JSONPath.
Solution 1:[1]
I tackled this kind of problem with my own small library which essentially mimics the XSLT processing model; so, here's a fiddle for your example:
https://jsfiddle.net/YSharpLanguage/2sa0twny
where:
var source =
{
store:'my store',
items: [{
name: 'Hammer',
skus: [{
num: '12345qwert'
}]
}, {
name: 'Bike',
skus: [{
num: 'asdfghhj'
}, {
num: 'zxcvbn'
}]
}, {
name: 'Fork',
skus: [{
num: '0987dfgh'
}]
}]
};
function Root(node) {
return (typeof node.store === "string") &&
({ }.toString.call(node.items) === "[object Array]");
}
function SKU(node) {
return typeof node.num === "string";
}
function FlattenedSKU(node) {
return SKU(node) && (typeof node.name === "string");
}
var SO_30221930_Transform = { $: [
[ [ Root ],
// rewriting rule to process JSONPath's "$"
// (XSLT-would-be <xsl:template match="/">...)
function(root) {
return {
// (cf. the `String.prototype.of` interpolation helper)
storeName: "{store}".of(root),
// (XSLT-would-be <xsl:apply-templates select="items/sku"/>)
items: root.items.
// first, get all the SKU nodes (XPath "items//sku")
nodeset(SKU, true).
// then, flatten them as [ { name: ..., num: ... } ... ]
map(function(sku_node) {
return {
name: "{name}".of(sku_node.parent.parent),
num: "{num}".of(sku_node.value),
};
}).
// finally, rewrite them as [ { uniqueName: ..., num: ... } ... ]
through(this)
}
}
],
[ [ FlattenedSKU ],
function(sku) {
return {
uniqueName: "{name}".of(sku),
num: "{num}".of(sku)
};
}
]
] };
var output = source.through(SO_30221930_Transform);
console.log(JSON.stringify(output, null, 2));
gives:
{
"storeName": "my store",
"items": [
{
"uniqueName": "Hammer",
"num": "12345qwert"
},
{
"uniqueName": "Bike",
"num": "asdfghhj"
},
{
"uniqueName": "Bike",
"num": "zxcvbn"
},
{
"uniqueName": "Fork",
"num": "0987dfgh"
}
]
}
Here are other examples / use cases:
'Hope this helps,
Solution 2:[2]
This may not be possible. You are really iterating over skus, not items, so in theory the expression should be something like this:
var output = {
storeName: '$.store',
items: ['$.items[*].skus[*]', { // or '$..skus[*]', or perhaps even '$..num'
uniqueName: '$<<name', // no "parent" operator in JSONPath
sku: '$.num'
}]
}
But there are two problems:
1) jsonpath-object-transform appears not to properly transform children: leaving out uniquename
, if you omit the transform, the correct num
entries are produced, but if you add a transform an exception is thrown. I assume this is a bug.
2) even if you could iterate skus
, JSONPath has no parent operator so there would be no way to access the current item .name
.
Anyway, that's my best assessment.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Tomalak |
Solution 2 |