I have following JSON
{"subscription":
{
"callbackReference": "xyz" ,
"criteria": "Vote",
"destinationAddress": "3456" ,
"notificationFormat" : "JSON"
}
}
I want to check whether "notificationFormat" elements exits there using JSONPath expression. I can get the value of above element using following JSONPath expression.
$.subscription.notificationFormat
Can I use similar kind of expression which returns boolean value checking whether elements exists ?
If I understood your question correct here is an answer.
This would check if notificationFormat exists in your json.
$.subscription[?(#.notificationFormat)]
That would get all destinationAddress in case if notificationFormat exists
$.subscription[?(#.notificationFormat)].destinationAddress
ReadContext ctx = JsonPath.parse("{}", com.jayway.jsonpath.Configuration.defaultConfiguration().addOptions(Option.SUPPRESS_EXCEPTIONS));
assertThat(ctx.read("$.components"), nullValue());
If you're using Jayway's Java implementation of JSONPath - the JsonPath library - and parsing the JSON once, ahead of time, to make multiple reads more efficient, then there is an arguably clearer (than using a JSONPath filter expression) way to check whether an optional JSON property exists. Use the ReadContext object representation of the parsed JSON to return the parent JSON object as a Java HashMap, then check whether a map entry exists with the property's name. Using the JSON from the question, the (Java) code to check whether the optional 'notificationFormat' property exists would be -
ReadContext parsedJson = JsonPath.parse(jsonString);
HashMap subscription = parsedJson.read("$.subscription");
if (subscription.containsKey("notificationFormat")) {
...
}
Related
I'm trying to check in my integration test if all of values from some specific property has the same type. I was trying to do it along with jsonPath and JsonPathResultMatchers but without success. Finally in I did something like this :
MvcResult result = mockMvc.perform(get("/weather/" + existingCity))
.andExpect(MockMvcResultMatchers.status().isOk())
.andReturn();
String responseContent = result.getResponse().getContentAsString();
TypeRef<List<Object>> typeRef = new TypeRef<List<Object>>() {
};
List<Object> humidities = JsonPath.using(configuration).parse(responseContent).read("$.*.humidity", typeRef);
Assertions.assertThat(humidities.stream().allMatch(humidity -> humidity instanceof Integer)).isTrue();
But I wonder if exist some clearer way to do this, can the same result be achieved with JSONPath ? Or AssertJ has some method to find it without usage stream code
Just answering on the AssertJ part: Stream assertions are provided with some caveats as the Stream under test is converted to a List in order to be able to perform multiple assertions (otherwise you can't as a Stream can only be consumed once).
Javadoc: assertThat(BaseStream)
Example:
assertThat(DoubleStream.of(1, 2, 3)).isNotNull()
.contains(1.0, 2.0, 3.0)
.allMatch(Double::isFinite);
I have happily used https://github.com/lukas-krecan/JsonUnit to check JSON, you can give it a try and see if you like it.
I personnaly would rather validate it against a JSON schema. There are Java validator implementations that could help you
After executing a MongoDB query my result is of type : res = Seq[Document]
To access the BsonString I use : res (0).get("n"))
Which returns :
Some(BsonString{value='value'})
How can I access the value value from the BsonString as a String ?
Accessing the value of Some(BsonString{value='value'}) returns BsonString{value='value'} do I need to convert BsonString{value='value'} to a Scala object using a library (for example Jackson) and then access the value ?
I suppose you are using the mongo scala driver (and not ReactiveMongo).
In that case, the returned BsonString is a java object; here is the scaladoc that points to the javadoc.
And you can access the value via the getValue method.
As you are getting back Option objects, I would recommend to use proper for comprehension to avoid runtime exceptions; something like:
val optionalResult = for {
doc <- res.headOption
element <- doc.get[BsonString]("n")
} yield (element.getValue)
optionalResult will be of type Option[String].
You can then check if you have a value and use it; via map, flatMap, foreach or even if (optionalResult.isDefined).
I am writing a simple perl script to read JSON from a file and insert into MongoDB. But I am facing issues with json decoding.
All non-string values in my original json are getting converted to object type after decode_json.
Input JSON(only part of it since it's original is huge) -
{
"_id": 2006010100000801089,
"show_image" : false,
"event" : "publish",
"publish_date" :1136091600,
"data_version" : 1
}
JSON that gets inserted to MongoDB -
{
"_id": NumberLong("2006010100000801089"),
"show_image" : BinData(0,"MA=="),
"event" : "publish",
"publish_date" :NumberLong(1136091600),
"data_version" : NumberLong(1)
}
I am providing the custom _id for the documents, which I want to get converted to NumberLong type. That is working as expected as you can see from the JSON above. But notice how other non-string values for show_image, publish_date and data_version got converted to it's object representation.
Is there any way I can retain the original type for these values?
Perl code snipper that does the insert -
use MongoDB;
use MongoDB::OID;
use JSON;
use JSON::XS
while(my $record = <$source_file>) {
my $record_decoded = decode_json($record);
$db_collection->insert($record_decoded);
}
Perl version used v5.18.2.
I looked up JSON::XS docs but couldn't find a way to do this. Any help is appreciated. Thanks in advance!
I am very new to perl. Sorry if this is a trivial question.
I am providing the custom _id for the documents, which I want to get converted to NumberLong type. That is working as expected as you can see from the JSON above. But notice how other non-string values for show_image, publish_date and data_version got converted to it's object representation.
From your example all of the data types are actually matching aside from the boolean value for show_image which is currently being converted to binary data.
It is expected that numeric types are displayed as NumberLong or NumberInt when queried from the mongo shell. The mongo shell uses JavaScript, which only has a single numeric type of Number (64-bit floating point). Shell helpers like NumberLong() and NumberInt() are used to represent values in MongoDB's BSON data types that do not have a native JavaScript equivalent.
Referring to my sample JSON, I want value of show_image to be inserted as false instead of BinData(0,"MA==") and publish_date to be inserted as 1136091600 instead of NumberLong(1136091600)
While it's OK to insert publish_date as a Unixtime if that suits your use case, you may find it more useful to use MongoDB's Date type instead. There are convenience methods for querying dates including Date Aggregation Operators. FYI, date fields will be displayed in the mongo shell with an ISODate() wrapper.
The boolean value for show_image definitely needs an assist, though.
If you use Data::Dumper to inspect the result from decode_json(), you will see that the show_image field is a blessed object:
'show_image' => bless( do{\(my $o = 0)}, 'JSON::PP::Boolean' )
In order to get the expected boolean value in MongoDB, the recommended approach in the MongoDB module docs is to use the boolean module (see: MongoDB::DataTypes).
I couldn't find an obvious built-in option for JSON or JSON::XS to support serialising booleans to something other than the JSON emulated boolean class, but one solution would be to use the Data::Clean::Base module which is part of the Data::Clean::JSON distribution.
Sample snippet (excluding the MongoDB set up):
use Data::Clean::Base;
use boolean;
my $cleanser = Data::Clean::Base->new(
'JSON::XS::Boolean' => ['call_func', 'boolean::boolean'],
'JSON::PP::Boolean' => ['call_func', 'boolean::boolean']
);
while (my $record = <$source_file>) {
my $record_decoded = decode_json($record);
$cleanser->clean_in_place($record_decoded);
$db_collection->insert($record_decoded);
}
Sample record as saved in MongoDB 3.0.2:
{
"_id": NumberLong("2006010100000801089"),
"event": "publish",
"data_version": NumberLong("1"),
"show_image": false,
"publish_date": NumberLong("1136091600")
}
JSON data contains only (double-precision) numbers, strings, and the special values true, false, and null. They can be arranged in arrays or "objects" (hashes).
The MongoDB engine is converting these basic types into something more complex, but the original values are available in the hash referred to by $record_decoded, like so
$record_decoded->{_id}
$record_decoded->{show_image}
$record_decoded->{event}
$record_decoded->{publish_date}
$record_decoded->{data_version}
Is that what you wanted?
The object serialization documentation (particularly allow_tags) in JSON::XS may do something like what you want. Note, though, that this is not a standard JSON feature and will only work with JSON::XS.
I have the following JSON and I need to get the plain name value using JSONPath:
{
"single" : {
"id" : 1,
"name" : "Item name"
}
}
Expression that I used is $.single.name but I always get an array:
[ "Item name" ]
instead of a string value ("Item name").
but I always get an array:
That is meant to happen. As you can read in this documentation, under 'Result' (almost at the bottom):
Please note, that the return value of jsonPath is an array, which is
also a valid JSON structure. So you might want to apply jsonPath to
the resulting structure again or use one of your favorite array
methods as sort with it.
So basically it will always return an array. If you need the data as an other type, e.g. a String in this case, you will have to do the conversion yourself I'm afraid.
I was using the Java implementation of JSONPath and got to the very same issue. What worked for me was to add '[0]' to the json path string. So in your case:
$.single.name[0]
I think, it depends of language implementation.
For example in nodejs there is a npm module : https://www.npmjs.com/package/jsonpath
Which have a method called value, which does exactly what we need
jp.value(obj, pathExpression[, newValue])
Returns the value of the first element matching pathExpression. If newValue is provided, sets the value of the first matching element and returns the new value.
I tried it and worked!
In case you got this error
"Unable to get a scalar value for expression $.yourfield"
You have just to configure the EvaluateJsonPath processor by changing the return type property value to 'json' instead of 'auto-detect'
I'm trying to build a restful API and I'm struggling on how to serialize JSON data to a HTTP query string.
There are a number of mandatory and optional arguments that need to be passed in the request, e.g (represented as a JSON object below):
{
"-columns" : [
"name",
"column"
],
"-where" : {
"-or" : {
"customer_id" : 1,
"services" : "schedule"
}
},
"-limit" : 5,
"return" : "table"
}
I need to support a various number of different clients so I'm looking for a standardized way to convert this json object to a query string. Is there one, and how does it look?
Another alternative is to allow users to just pass along the json object in a message body, but I read that I should avoid it (HTTP GET with request body).
Any thoughts?
Edit for clarification:
Listing how some different languages encodes the given json object above:
jQuery using $.param: -columns[]=name&-columns[]=column&-where[-or][customer_id]=1&-where[-or][services]=schedule&-limit=5&return=column
PHP using http_build_query: -columns[0]=name&-columns[1]=column&-where[-or][customer_id]=1&-where[-or][services]=schedule&-limit=5&return=column
Perl using URI::query_form: -columns=name&-columns=column&-where=HASH(0x59d6eb8)&-limit=5&return=column
Perl using complex_to_query: -columns:0=name&-columns:1=column&-limit=5&-where.-or.customer_id=1&-where.-or.services=schedule&return=column
jQuery and PHP is very similar. Perl using complex_to_query is also pretty similar to them. But none look exactly the same.
URL-encode (https://en.wikipedia.org/wiki/Percent-encoding) your JSON text and put it into a single query string parameter. for example, if you want to pass {"val": 1}:
mysite.com/path?json=%7B%22val%22%3A%201%7D
Note that if your JSON gets too long then you will run into a URL length limitation problem. In which case I would use POST with a body (yes, I know, sending a POST when you want to fetch something is not "pure" and does not fit well into the REST paradigm, but neither is your domain specific JSON-based query language).
There is no single standard for JSON to query string serialization, so I made a comparison of some JSON serializers and the results are as follows:
JSON: {"_id":"5973782bdb9a930533b05cb2","isActive":true,"balance":"$1,446.35","age":32,"name":"Logan Keller","email":"logankeller#artiq.com","phone":"+1 (952) 533-2258","friends":[{"id":0,"name":"Colon Salazar"},{"id":1,"name":"French Mcneil"},{"id":2,"name":"Carol Martin"}],"favoriteFruit":"banana"}
Rison: (_id:'5973782bdb9a930533b05cb2',age:32,balance:'$1,446.35',email:'logankeller#artiq.com',favoriteFruit:banana,friends:!((id:0,name:'Colon Salazar'),(id:1,name:'French Mcneil'),(id:2,name:'Carol Martin')),isActive:!t,name:'Logan Keller',phone:'+1 (952) 533-2258')
O-Rison: _id:'5973782bdb9a930533b05cb2',age:32,balance:'$1,446.35',email:'logankeller#artiq.com',favoriteFruit:banana,friends:!((id:0,name:'Colon Salazar'),(id:1,name:'French Mcneil'),(id:2,name:'Carol Martin')),isActive:!t,name:'Logan Keller',phone:'+1 (952) 533-2258'
JSURL: ~(_id~'5973782bdb9a930533b05cb2~isActive~true~balance~'!1*2c446.35~age~32~name~'Logan*20Keller~email~'logankeller*40artiq.com~phone~'*2b1*20*28952*29*20533-2258~friends~(~(id~0~name~'Colon*20Salazar)~(id~1~name~'French*20Mcneil)~(id~2~name~'Carol*20Martin))~favoriteFruit~'banana)
QS: _id=5973782bdb9a930533b05cb2&isActive=true&balance=$1,446.35&age=32&name=Logan Keller&email=logankeller#artiq.com&phone=+1 (952) 533-2258&friends[0][id]=0&friends[0][name]=Colon Salazar&friends[1][id]=1&friends[1][name]=French Mcneil&friends[2][id]=2&friends[2][name]=Carol Martin&favoriteFruit=banana
URLON: $_id=5973782bdb9a930533b05cb2&isActive:true&balance=$1,446.35&age:32&name=Logan%20Keller&email=logankeller#artiq.com&phone=+1%20(952)%20533-2258&friends#$id:0&name=Colon%20Salazar;&$id:1&name=French%20Mcneil;&$id:2&name=Carol%20Martin;;&favoriteFruit=banana
QS-JSON: isActive=true&balance=%241%2C446.35&age=32&name=Logan+Keller&email=logankeller%40artiq.com&phone=%2B1+(952)+533-2258&friends(0).id=0&friends(0).name=Colon+Salazar&friends(1).id=1&friends(1).name=French+Mcneil&friends(2).id=2&friends(2).name=Carol+Martin&favoriteFruit=banana
The shortest among them is URL Object Notation.
How about you try this sending them as follows:
http://example.com/api/wtf?
[-columns][]=name&
[-columns][]=column&
[-where][-or][customer_id]=1&
[-where][-or][services]=schedule&
[-limit]=5&
[return]=table&
I tried with a REST Client
And on the server side (Ruby with Sinatra) I checked the params, it gives me exactly what you want. :-)
Another option might be node-querystring. It also uses a similar scheme to the ones you've so far listed.
It's available in both npm and bower, which is why I have been using it.
Works well for nested objects.
Passing complex objects as query parameters of a url.
In the example below, obj is the JSON object to pass into query parameters.
Injecting JSON object as query parameters:
value = JSON.stringify(obj);
URLSearchParams to convert a string to an object representing search params. toString to retain string type for appending to url:
queryParams = new URLSearchParams(value).toString();
Pass the query parameters using template literals:
url = `https://some-url.com?key=${queryParams}`;
Now url will contain the JSON object as query parameters under key (user-defined name)
Extracing JSON from url:
This is assuming you have access to the url (either as string or URL object)
url_obj = new URL(url); (only if url is NOT a URL object, otherwise ignore this step)
Extract all query parameters in the url:
queryParams = new URLSearchParams(url_obj.search);
Use the key to extract the specific value:
obj = JSON.parse(queryParams.get('key').slice(0, -1));
slice() is used to extract a tailing = in the query params which is not required.
Here obj will be the same object passed in the query params.
I recommend to try these steps in the web console to understand better.
You can test with JSON examples here: https://json.org/example.html