How to get each field of json doc in XQUERY Marklogic? - json

How to get each field of json doc in XQUERY Marklogic?
let $doc :=
{
"field1" :'t',
"field2" : 'th',
"filed4": 'the'
}
return
$doc//??,
{
"New Filed" : "Added"
}
So how can we get the output like below ?
{ "field1" :'t', "field2" : 'th', "filed4": 'the' ,"New Filed" : "Added"}

One approach: use the xdmp:from-json() to convert the immutable JSON node to a mutable map and then set the field:
return xdmp:from-json($doc) => map:with("NewField", "Added")
For more detail, see: https://docs.marklogic.com/xdmp:from-json
Hoping that helps,

A JSON object is really just a specialized map. So you can use map operators, like the + union operator:
let $doc := object-node
{
"field1" :'t',
"field2" : 'th',
"filed4": 'the'
}
return
$doc + map:entry("NewField", "added")

Related

Karate -- JSON Response Parsing

Below is the JSON response I receive when I am hitting a particular web service:
[
{
"sId" : "0001",
"sName" : "abc1",
"sPlace" : "abc11"
}, {
"sId" : "0002",
"sName" : "abc2",
"sPlace" : "abc12"
}, {
"sId" : "0003",
"sName" : "abc3",
"sPlace" : "abc13"
}, {
"sId" : "0004",
"sName" : "abc4",
"sPlace" : "abc14"
}
]
I don't know which index has my expected values (I need to validate multiple values after identifying which has sId == '0003'), this is dynamic. Don't want to user hard coded value.
And match response.[3].sId == '0003'
because this will be changed next time.
I have two questions regarding this:
How can I pass response to java code and get the array index which having sId == '0003' so that I can use this index to validate?
How can I pass a variable value as an array index in response?
The code below is not working.
def ind = Java.type('karate.Utility.FindIndex')
response.['#ind'].sId == '0003'
karate uses json-path which allows writing conditions to read data from JSON.
example:
* def sId = "0003"
* def sValue = karate.jsonPath(response, "$[?(#.sId == '" + sId + "')]")
* match sValue[0] == {"sId" : "0003","sName" : "abc3","sPlace" : "abc13"}
now if there is a match in sId on the response JSON array, all such matches will be returned.
No need to do * match sValue[0].sId == "0003" as this is your filter
criteria
More about JSON path
online JSON path evaluator
karate doc refernce

Transform XML elements of the same name into JSON properties of the same name

XML elements and JSON properties of the same name may be siblings. I.e.:
<container>
<value>value1</value>
<value>value2</value>
</container>
and
object-node {
"value" : "value1",
"value" : "value2"
}
are both valid, but I have not found a valid way to transform one into the other. It's invalid to dynamically build properties within an object-node constructor, i.e.:
object-node {
for $v in $values
return 'value' : $v
}
Using maps doesn't work because the duplicate key names are collapsed:
xdmp:to-json(map:new((
map:entry("value", "value1"),
map:entry("value", "value2")))
)
=> {"value":"value2"}
And when using json:object, the last key value is duplicated:
json:object(<json:object>
<json:entry key="value">
<json:value>value1</json:value>
</json:entry>
<json:entry key="value">
<json:value>value2</json:value>
</json:entry>
</json:object>)
=> {"value":"value2", "value":"value2"}
Joining maps using the + operator is better, but it merges the duplicate keys into a single key with an array of values ({"value":["value1", "value2"]}), which is still not what I want. Is there any way to build sibling JSON properties of the same name dynamically in XQuery?
Your JSON example:
object-node {
"value" : "value1",
"value" : "value2"
}
isn't really valid: or at any rate, it's best avoided. RFC 7159 says:
When the names within an object are not unique, the behavior of
software that receives such an object is unpredictable. Many
implementations report the last name/value pair only. Other
implementations report an error or fail to parse the object, and
some implementations report all of the name/value pairs, including
duplicates.
I don't believe that it is possible to embed a FLWOR inside of the object-node() constructor.
You could construct a string and evaluate with xdmp:eval() or xdmp:value():
let $container :=
<container>
<value>value1</value>
<value>value2</value>
</container>
return
xdmp:value(
"object-node {" ||
fn:string-join($container/value ! ('"' || local-name() || '": "' || . || '"'), ",") ||
" }"
)
Or build the JSON string and use xdmp:unquote():
let $container :=
<container>
<value>value1</value>
<value>value2</value>
</container>
return
xdmp:unquote(
"{" ||
fn:string-join($container/value ! ('"' || local-name() ||'": "' || . || '"'), ",") ||
"}"
)

JSONiq count duplicates in array

I want to count duplicate values with JSONiq. I have following code:
jsoniq version "1.0";
import module namespace fetch = "http://zorba.io/modules/fetch";
let $tweets := parse-json(fetch:content("/tweets.json"))
let $users := parse-json(fetch:content("/users.json"))
return
let $different_languages :=
for $tweet in $tweets[]
return {
"name" : $tweet."metadata"."iso_language_code"
}
return [$different_languages]
This returns all the languages, but it opens a new pair for every language. It looks like this:
[ { "name" : "de" },
{ "name" : "da" },
{ "name" : "da" },
{ "name" : "da" }]
I want to return a JSON object that looks like this:
[ { "count" : 1, "language" : "de" },
{ "count" : 3, "language" : "da" }]
How can i achieve this?
This can be achieved with a group-by clause. This works like a SQL group by, but with an even finer level of control.
In the code below, the four objects in the unboxed $tweets array are grouped according to their language field ($tweet.metadata.iso_language_code). In each evaluation of the return clause, the grouping variable $language will contain the name of the language for the current group, and the non-grouping variable $tweet will contain the sequence of tweets that belong to the group. Calling count() on this sequence will return 3 and 1 respectively.
jsoniq version "1.0";
import module namespace fetch = "http://zorba.io/modules/fetch";
let $tweets := parse-json(fetch:content("/tweets.json"))
let $users := parse-json(fetch:content("/users.json"))
return
for $tweet in $tweets[]
group by $language := $tweet."metadata"."iso_language_code"
return { language: $language, count: count($tweet) }
Also, the quotes on metadata and iso_language_code are not needed. If needed, you can also wrap the result into an array, like so:
jsoniq version "1.0";
import module namespace fetch = "http://zorba.io/modules/fetch";
let $tweets := parse-json(fetch:content("/tweets.json"))
let $users := parse-json(fetch:content("/users.json"))
return [
for $tweet in $tweets[]
group by $language := $tweet.metadata.iso_language_code
return { language: $language, count: count($tweet) }
]
There is no restriction on the expression in the return clause: $language and $tweet are variables like any others and they can be used as inputs to any JSONiq expression. For instance, instead of counting the tweets, one can also nest them in the output, as the data model is arborescent:
return { language: $language, count: [ $tweet ] }

How to get sum of nested JSON elements in Long format using scala and play?

I have nested JSON like -
"disks" : [ {
"name" : "v2.16",
"diskAggregate" : "aggr0",
"diskRPM" : 15000,
"totalSizeBytes" : 1077477376,
"vendorId" : "NETAPP ",
"usedBytes" : 1070071808,
"diskType" : "FCAL",
"uuid" : "4E455441:50502020:56442D31:3030304D:422D465A:2D353230:32353836:30303030:00000000:00000000",
"portName" : "FC:A ",
"raidGroup" : "rg0"
},
{
"name" : "v4.16",
"diskAggregate" : "aggr0",
"diskRPM" : 15000,
"totalSizeBytes" : 1077477376,
"vendorId" : "NETAPP ",
"usedBytes" : 1070071808,
"diskType" : "FCAL",
"uuid" : "4E455441:50502020:56442D31:3030304D:422D465A:2D353230:32353633:34333030:00000000:00000000",
"portName" : "FC:B ",
"raidGroup" : "rg0"
}]
I want to get addition 'totalSizeBytes' from above list of objects.
I used following code to get it -
val storageDevices = "above given json".toList
val totalCapacity = storageDevices.foldLeft(0) {
case (sumOfAllDevices, storageDevice) =>
val sumOfTotalBytesOnStorageDevice = storageDevice.disks.foldLeft(0) {
case (totalBytesOnDevice, disk) =>
totalBytesOnDevice + disk.usedBytes.getOrElse(0).toString.toInt
}
sumOfAllDevices + sumOfTotalBytesOnStorageDevice
// Logger.info("dss"+sumOfTotalBytesOnStorageDevice.toString.toInt)
}
This code gives me total capacity in Integer format. But as there are too many objects in disks array, the totalCapacity will get exceed int. So I wanted to convert it to Long while doing addition.
I want following output-
"totalCapacity": [
{
"name": "192.168.20.22",
"y": 123456789
}
]
How do I convert it to Long to get exact sum of all 'totalBytesAvailable' from array/list???
Cast zero values as 0L (by default assumed Int), both in foldLeft(0L) and in getOrElse(0L), so the compiler will enforce arithmetic additions on Long.

What is "compressed JSON"?

I see a lot of references to "compressed JSON" when it comes to different serialization formats. What exactly is it? Is it just gzipped JSON or something else?
Compressed JSON removes the key:value pair of json's encoding to store keys and values in seperate parallel arrays:
// uncompressed
JSON = {
data : [
{ field1 : 'data1', field2 : 'data2', field3 : 'data3' },
{ field1 : 'data4', field2 : 'data5', field3 : 'data6' },
.....
]
};
//compressed
JSON = {
data : [ 'data1','data2','data3','data4','data5','data6' ],
keys : [ 'field1', 'field2', 'field3' ]
};
This method of usage i found here
Content from link (http://www.nwhite.net/?p=242)
rarely find myself in a place where I am writing javascript applications that use AJAX in its pure form. I have long abandoned the ‘X’ and replaced it with ‘J’ (JSON). When working with Javascript, it just makes sense to return JSON. Smaller footprint, easier parsing and an easier structure are all advantages I have gained since using JSON.
In a recent project I found myself unhappy with the large size of my result sets. The data I was returning was tabular data, in the form of objects for each row. I was returning a result set of 50, with 19 fields each. What I realized is if I augment my result set I could get a form of compression.
// uncompressed
JSON = {
data : [
{ field1 : 'data1', field2 : 'data2', field3 : 'data3' },
{ field1 : 'data4', field2 : 'data5', field3 : 'data6' },
.....
]
};
//compressed
JSON = {
data : [ 'data1','data2','data3','data4','data5','data6' ],
keys : [ 'field1', 'field2', 'field3' ]
};
I merged all my values into a single array and store all my fields in a separate array. Returning a key value pair for each result cost me 8800 byte (8.6kb). Ripping the fields out and putting them in a separate array cost me 186 bytes. Total savings 8.4kb.
Now I have a much more compressed JSON file, but the structure is different and now harder to work with. So I implement a solution in Mootools to make the decompression transparent.
Request.JSON.extend({
options : {
inflate : []
}
});
Request.JSON.implement({
success : function(text){
this.response.json = JSON.decode(text, this.options.secure);
if(this.options.inflate.length){
this.options.inflate.each(function(rule){
var ret = ($defined(rule.store)) ? this.response.json[rule.store] : this.response.json[rule.data];
ret = this.expandData(this.response.json[rule.data], this.response.json[rule.keys]);
},this);
}
this.onSuccess(this.response.json, text);
},
expandData : function(data,keys){
var arr = [];
var len = data.length; var klen = keys.length;
var start = 0; var stop = klen;
while(stop < len){
arr.push( data.slice(start,stop).associate(keys) );
start = stop; stop += klen;
}
return arr;
}
});
Request.JSON now has an inflate option. You can inflate multiple segments of your JSON object if you so desire.
Usage:
new Request.JSON({
url : 'url',
inflate : [{ 'keys' : 'fields', 'data' : 'data' }]
onComplete : function(json){}
});
Pass as many inflate objects as you like to the option inflate array. It has an optional property called ’store’ If set the inflated data set will be stored in that key instead.
The ‘keys’ and ‘fields’ expect strings to match a location in the root of your JSON object.
Based in Paniyar's answer, we can convert a List of Objects in "compressed" Json format using C# like this:
var JsonString = serializer.Serialize(
new
{
cols = new[] { "field1", "field2", "field3"},
items = data.Select(x => new object[] {x.field1, x.field2, x.field3})
});
I used an array of object because the fields can be int, bool, string...
More Reduction:
If the field is repeated very often and it is a string type, you can get compressed a little be more if you add a distinct list of that field... for instance, a field name job position, city, etc are excellent candidate for this. You can add a distinct list of this items and in each item change the value for a reference number. That will make your Json more lite.
Compressed:
[["KeyA", "KeyB", "KeyC", "KeyD", "KeyE", "KeyF"],
["ValA1", "ValB1", "ValC1", "ValD1", "ValE1", "ValF1"],
["ValA2", "ValB2", "ValC2", "ValD2", "ValE2", "ValF2"],
["ValA3", "ValB3", "ValC3", "ValD3", "ValE3", "ValF3"],
["ValA4", "ValB4", "ValC4", "ValD4", "ValE4", "ValF4"]]
Uncompressed:
[{KeyA: "ValA1", KeyB: "ValB1", KeyC: "ValC1", KeyD: "ValD1", KeyE: "ValE1", KeyF: "ValF1"},
{KeyA: "ValA2", KeyB: "ValB2", KeyC: "ValC2", KeyD: "ValD2", KeyE: "ValE2", KeyF: "ValF2"},
{KeyA: "ValA3", KeyB: "ValB3", KeyC: "ValC3", KeyD: "ValD3", KeyE: "ValE3", KeyF: "ValF3"},
{KeyA: "ValA4", KeyB: "ValB4", KeyC: "ValC4", KeyD: "ValD4", KeyE: "ValE4", KeyF: "ValF4"}]
The most likely answer is that it really is just gzipped JSON. There is no other standard meaning to this phrase.
Re-organizing a homogenous array of JSON objects into a pair of arrays is a very useful technique to make the payload smaller and to speed up encoding and decoding, it is not commonly called "compressed JSON". I haven't run across it ever in open source or any open API, but we use this technique internally and call it "jsontable".