Extracting a subset of attributes with JSONPath - json

I have this JSON code:
{
"A": {
"AB": [{
"ABA": "0",
"ABB": "1",
"ABC": "2"
}]
}
}
I need to use a JSONPath expression that returns that JSON with only ABA and ABC attributes. Something like:
{
"A": {
"AB": [{
"ABA": "0",
"ABC": "2"
}]
}
}
So far I manage to extract either one or all attributes. For example
$.A.AB[*]
or
$.A.AB[*].ABA
Is there a way to extract only two?
Thanks

This will work using the Jayway implementation (Java):
$.A.AB[*]['ABB', 'ABA']
and the result for your input would be:
[
{
"ABB" : "1",
"ABA" : "0"
}
]
You can Compare different providers here:
http://jsonpath.herokuapp.com/

Related

How to read nested array elements from JSON?

I need to parse a JSON with nested array elements and extract the values.
I am not sure how to use the nested array to set the value of an attribute in output JSON.
This is the input:
[{
"name": "book1",
"id": 18789,
"locations": [{
"state": "mystate",
"phone": 8877887700
}, {
"state": "mystate1",
"phone": 8877887701
}]
},
{
"name": "book2",
"id": 18781,
"locations": [{
"state": "mystate3",
"phone": 8877887711
}, {
"state": "mystate4",
"phone": 8877887702
}]
}]
And this is the expected output:
{
"name": ["book1", "book2"],
"id": ["18789", "18781"],
"states": [
["mystate", "mystate"],
["mystate3", "mystate4"]
]
}
I am trying to use the following JSLT expression:
{
"name" : [for (.)
let s = string(.name)
$s],
"id": [for (.)
let s = string(.id)
$s],
"states": [for (.)
let s = string(.locations)
$s]
}
But I am not sure how to set the states in this case so that I have the value of state in the output.
A solution using JQ or JSONPath may also help.
With JQ it'd be easier than that.
{
name: map(.name),
id: map(.id),
states: map(.locations | map(.state))
}
Online demo
In JSLT you can implement it like this:
{
"name" : [for (.) .name],
"id": [for (.) .id],
"states": flatten([for (.) [for (.locations) .state]])
}
The states key is a bit awkward to implement, as you see. I have thought of making it possible to let path expressions traverse arrays, and if we add that to the language it could be implemented like this:
{
"name" : .[].name,
"id": .[].id,
"states": .[].locations.[].state
}

Whats the standard of defining an empty object in JSON

I have an issue with my application. It is returning a JSON file of an array of objects. The application is defining an empty object inside the array of objects as text value string whose value is defined as an object in the other element of array. Please see the value of the key "b" in the example.
For Eg:
{
"result": [{
"a": "1",
"b": {
"c1": "31",
"c2": "32"
}
}, {
"a": "5",
"b": ""
}
]
}
I want to know if that is a correct way of defining the key "b" as an empty object.
Thanks in advance!!
An empty object is defined by {}:
"b": {}
I.e. use the usual object delimiters but don't add any key-values.
What you defined is an empty string.
In JSON, an object is defined with { }, which is exactly what you would represent an empty object as.
{
"result": [
{
"a": "1",
"b": {
"c1": "31",
"c2": "32"
}
}, {
"a": "5",
"b": { }
}
]
}

Trouble Parsing Array vs Non-Array JSON with JSONPath

I have JSON that looks like the below. I'm trying to use JSONPath to grab the __ content __ value where the SKU is "8A-OK9F-9LI8" AND the Component.Type == 'Principal'. Right now, I am playing around with this JSON Path Expression Tester.
This JSONPath expression grabs all of the component information I need:
$.Order..Fulfillment[?(#.SKU=='8A-OK9F-9LI8')]..Component
But filtering further such as $.Order..Fulfillment[?(#.SKU=='8A-OK9F-9LI8')]..Component[?(#.Type=='Principal')] grabs only one (I believe the Array one) of the two Component elements I need. I suspect this is because one is an Array and one is a single JSON element. Is it possible to grab this with one command or do I have to combine several commands (one for the Array and one for the single JSON element)? If so, how can I grab the other Component information that I am not currently getting with:
$.Order..Fulfillment[?(#.SKU=='8A-OK9F-9LI8')]..Component[?(#.Type=='Principal')]?
Again, my goal is to grab the "__ content__" value and filter by a specific SKU and where the Component.Type == 'Principal'. Something like:
$.Order..Fulfillment[?(#.SKU=='8A-OK9F-9LI8')]..Component[?(#.Type=='Principal')]..Amount..__content__
I'm expecting to get back ["8.49", "8.49"]
Here is the JSON I am testing with:
{
"SettlementData": {},
"Order": [
{
"OrderID": "XXX",
"Fulfillment": {
"Item": {
"SKU": "8A-OK9F-9LI8",
"Quantity": "1",
"ItemPrice": {
"Component": [
{
"Type": "Principal",
"Amount": {
"__content__": "8.49",
"currency": "USD"
}
},
{
"Type": "Tax",
"Amount": {
"__content__": "0.74",
"currency": "USD"
}
}
]
}
}
}
},
{
"OrderID": "XXX",
"Fulfillment": {
"Item": {
"SKU": "8A-OK9F-9LI8",
"Quantity": "1",
"ItemPrice": {
"Component": {
"Type": "Principal",
"Amount": {
"__content__": "8.49",
"currency": "USD"
}
}
}
}
}
}
]
}
I was able to solve this in two passes. In this example, #{sku} is a Ruby interpolated string that contains the SKU I am passing in:
$.Order..Fulfillment[?(#.SKU=='#{sku}')]..ItemPrice..[?(#.Type=='Principal')].Amount.__content__
$.Order..Fulfillment..Item[?(#.SKU=='#{sku}')]..ItemPrice..[?(#.Type=='Principal')].Amount.__content__
Using a Ruby gem "jsonpath", I was able to get the amounts I needed like this:
amount = JsonPath.on(settlement, "$.Order..Fulfillment[?(#.SKU=='#{sku}')]..ItemPrice..[?(#.Type=='Principal')].Amount.__content__")
.map(&:to_f).inject(:+)
amount2 = JsonPath.on(settlement, "$.Order..Fulfillment..Item[?(#.SKU=='#{sku}')]..ItemPrice..[?(#.Type=='Principal')].Amount.__content__")
.map(&:to_f).inject(:+)

JSONPath expression to look inside different keys

I have a JSON returned by a RESTful API:
{
"0": {
"id": "1484763",
"name": "Name",
"values": {
"0": {
"value": "Peter"
}
}
},
"1": {
"id": "2584763",
"name": "phone",
"values": {
"0": {
"value": "45456456"
}
}
}
}
How do I write a JSONPath that extracts a phone number value? (so in this case, "45456456"). What makes the problem harder, phone number object is not always inside "1" key.
Try this JsonPath which results in phone number 45456456
$..[?(#.name = 'phone')].values.0.value
Basically, you have to apply a filter ?() where name == 'phone' and then use normal json path.
Try the expression in this link
JsonPath expression syntax can be found here.

JSON Array Structure Variations

Below are 3 JSON Array structure formats...
The first one, the one outlined at JSON.org, is the one I am familiar with:
Format #1
{"People": [
{
"name": "Sally",
"age": "10"
},
{
"name": "Greg",
"age": "10"
}
]}
The second one is a slight variation that names the elements of the array. I personally don't care for it; you don't name elements of an array in code (they are accessed by index), why name them in JSON?
Format #2
{"People": [
"Person1": {
"name": "Sally",
"age": "10"
},
"Person2": {
"name": "Greg",
"age": "10"
}
]}
This last one is another variation, quite similar to Format #2, but I have a hunch this one is incorrect because it appears to have extra curly braces where they do not belong.
Format #3
{"People": [
{
"Person1": {
"name": "Sally",
"age": "10"
}
},
{
"Person2": {
"name": "Greg",
"age": "10"
}
}
]}
Again, I'm confident that Format #1 is valid as it is the JSON Array format outlined at JSON.org. However, what about Format #2 and Format #3? Are either of those considered valid JSON? If yes, where did those formats come from? I do not see them outlined at JSON.org or on Wikipedia.
Both #1 and #3 are (nearly - there are commas missing) valid JSON, but encode different structures:
#1 gives you an Array of Objects, each with name and age String properties
#3 gives you an Array of Objects, each with a single Object property, each with name and age String properties.
The #2 is invalid: Arrays (as defined by [ ... ]) may not contain property names.
Solution For Format#1
By default:
array=[];
object={};
JSON Code:
var Json = {
People:[]
};
Json.People.push({
"name": "Sally",
"age": "10"
});
Json.People.push({
"name": "Greg",
"age": "10"
});
JSON Result:
{"People":
[
{
"name": "Sally",
"age": "10"
},
{
"name": "Greg",
"age": "10"
}
]
}