Convert Nested Dictionary to a graphable format - json

So I'm trying to convert a nested dictionary like:
A = {
"root":
{
"child1":
{
"child11":"hmm",
"child12":"not_hmm"
},
"child2":"hello"
}
}
To this:
{
"name":"root",
"children": [
{"name":"child1",
"children" :
[{"name":"child11",
"children":[{"name":"hmm"}]}
{"name":"child12",
"children":[{"name":"not_hmm"}]}
]
},
{"name":"child2",
"children":[{"name":"hello"}]
}
]
}
I need this, since I'm trying to visualize it with this graph drawing template: Collapsible Tree
I'm having some trouble creating a recursive method that is capable of this transformation.
Preferably in python3. So far I have:
def visit(node, parent=None):
B = {}
for k,v in node.items():
B["name"]=k
B["children"] = []
if isinstance(v,dict):
print("Key value pair is",k,v)
B["children"].append(visit(v,k))
new_dict = {}
new_dict["name"]=v
return [new_dict]
C = visit(A) # This should have the final result
But its wrong. Any help is appreciated.

We'll have a function that takes a root (assuming it has only one entry), and returns a dict, as well as a helper function that returns lists of dicts.
def convert(d):
for k, v in d.items():
return {"name": k, "children": convert_helper(v)}
def convert_helper(d):
if isinstance(d, dict):
return [{"name": k, "children": convert_helper(v)} for k, v in d.items()]
else:
return [{"name": d}]
which gives us
json.dumps(convert(A), indent=2)
{
"name": "root",
"children": [
{
"name": "child1",
"children": [
{
"name": "child11",
"children": [
{
"name": "hmm"
}
]
},
{
"name": "child12",
"children": [
{
"name": "not_hmm"
}
]
}
]
},
{
"name": "child2",
"children": [
{
"name": "hello"
}
]
}
]
}

Related

How to get the All index values in Groovy JSON xpath

Please find the attached Groovy code which I am using to get the particular filed from the response body.
Query 1 :
It is retrieving the results when the I am using the correct Index value like if the data.RenewalDetails[o], will give output as Value 1 and if the data.RenewalDetails[1], output as Value 2.
But in my real case, I will never know about number of blocks in the response, so I want to get all the values that are satisficing the condition, I tried data.RenewalDetails[*] but it is not working. Can you please help ?
Query 2:
Apart from the above condition, I want to add one more filter, where "FamilyCode": "PREMIUM" in the Itemdetails, Can you help on the same ?
def BoundId = new groovy.json.JsonSlurper().parseText('{"data":{"RenewalDetails":[{"ExpiryDetails":{"duration":"xxxxx","destination":"LHR","from":"AUH","value":2,"segments":[{"valudeid":"xxx-xx6262-xxxyyy-1111-11-11-1111"}]},"Itemdetails":[{"BoundId":"Value1","isexpired":true,"FamilyCode":"PREMIUM","availabilityDetails":[{"travelID":"AAA-AB1234-AAABBB-2022-11-10-1111","quota":"X","scale":"XXX","class":"X"}]}]},{"ExpiryDetails":{"duration":"xxxxx","destination":"LHR","from":"AUH","value":2,"segments":[{"valudeid":"xxx-xx6262-xxxyyy-1111-11-11-1111"}]},"Itemdetails":[{"BoundId":"Value2","isexpired":true,"FamilyCode":"PREMIUM","availabilityDetails":[{"travelID":"AAA-AB1234-AAABBB-2022-11-10-1111","quota":"X","scale":"XXX","class":"X"}]}]}]},"warnings":[{"code":"xxxx","detail":"xxxxxxxx","title":"xxxxxxxx"}]}')
.data.RenewalDetails[0].Itemdetails.find { itemDetail ->
itemDetail.availabilityDetails[0].travelID.length() == 33
}?.BoundId
println "Hello " + BoundId
Something like this:
def txt = '''\
{
"data": {
"RenewalDetails": [
{
"ExpiryDetails": {
"duration": "xxxxx",
"destination": "LHR",
"from": "AUH",
"value": 2,
"segments": [
{
"valudeid": "xxx-xx6262-xxxyyy-1111-11-11-1111"
}
]
},
"Itemdetails": [
{
"BoundId": "Value1",
"isexpired": true,
"FamilyCode": "PREMIUM",
"availabilityDetails": [
{
"travelID": "AAA-AB1234-AAABBB-2022-11-10-1111",
"quota": "X",
"scale": "XXX",
"class": "X"
}
]
}
]
},
{
"ExpiryDetails": {
"duration": "xxxxx",
"destination": "LHR",
"from": "AUH",
"value": 2,
"segments": [
{
"valudeid": "xxx-xx6262-xxxyyy-1111-11-11-1111"
}
]
},
"Itemdetails": [
{
"BoundId": "Value2",
"isexpired": true,
"FamilyCode": "PREMIUM",
"availabilityDetails": [
{
"travelID": "AAA-AB1234-AAABBB-2022-11-10-1111",
"quota": "X",
"scale": "XXX",
"class": "X"
}
]
}
]
}
]
},
"warnings": [
{
"code": "xxxx",
"detail": "xxxxxxxx",
"title": "xxxxxxxx"
}
]
}'''
def json = new groovy.json.JsonSlurper().parseText txt
List<String> BoundIds = json.data.RenewalDetails.Itemdetails*.find { itemDetail ->
itemDetail.availabilityDetails[0].travelID.size() == 33 && itemDetail.FamilyCode == 'PREMIUM'
}?.BoundId
assert BoundIds.toString() == '[Value1, Value2]'
Note, that you will get the BoundIds as a List
If you amend your code like this:
def json = new groovy.json.JsonSlurper().parse(prev.getResponseData()
you would be able to access the number of returned items as:
def size = json.data.RenewalDetails.size()
as RenewalDetails represents a List
Just add as many queries you want using Groovy's && operator:
find { itemDetail ->
itemDetail.availabilityDetails[0].travelID.length() == 33 &&
itemDetail.FamilyCode.equals('PREMIUM')
}
More information:
Apache Groovy - Parsing and producing JSON
Apache Groovy: What Is Groovy Used For?

Preparing JSON array of Objects from multiple array list

I am very new to the Groovy scripts and would like to build a JSON output from the below JSON input. Kindly help!
My JSON input looks like this:
{
"id":"1222",
"storageNode": {
"uuid": "22255566336",
"properties": {
"BuinessUnit": [
"Light",
"Fan",
"Watch"
],
"Contact": [
"abc#gmail.com",
"fhh#gmail.com"
],
"Location": [
"Banglore",
"Surat",
"Pune"
]
}
}
}
Expected Output:
[
{
"BuinessUnit": "Light",
"Contact": "abc#gmail.com",
"Location": "Banglore"
},
{
"BuinessUnit": "Fan",
"Contact": "fhh#gmail.com",
"Location": "Surat"
},
{
"BuinessUnit": "Watch",
"Contact": "",
"Location": "Pune"
}
]
Please note that in case any array is not matching the value count that will always be the last one and in that case, a blank value ("") has to be populated. The "BusinessUnit" object can be referred for array size validation.
My code looks like this:
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
import groovy.json.*;
def Message processData(Message message) {
//Body
def body = message.getBody(String.class);
def jsonSlurper = new JsonSlurper()
def list = jsonSlurper.parseText(body)
String temp
def BU = list.storageNode.properties.get("BusinessUnit")
def builder = new JsonBuilder(
BU.collect {
[
BusinessUnit: it
]
}
)
message.setBody(builder.toPrettyString())
return message
}
It is only returning this:
[
{
"BusinessUnit": "Light"
},
{
"BusinessUnit": "Fan"
},
{
"BusinessUnit": "Watch"
}
]
Now how will I add other parts to it? Please help!
I have come up with the following solution that converts source JSON string to the target JSON string:
import groovy.json.JsonBuilder
import groovy.json.JsonSlurper
def json = '''
{
"id":"1222",
"storageNode": {
"uuid": "22255566336",
"properties": {
"BusinessUnit": [
"Light",
"Fan",
"Watch"
],
"Contact": [
"abc#gmail.com",
"fhh#gmail.com"
],
"Location": [
"Banglore",
"Surat",
"Pune"
]
}
}
}
'''
println convert(json)
String convert(String json) {
def list = new JsonSlurper().parseText(json)
List<String> units = list.storageNode.properties.BusinessUnit
List<String> contacts = list.storageNode.properties.Contact
List<String> locations = list.storageNode.properties.Location
def result = []
units.eachWithIndex { unit, int index ->
result << [
BusinessUnit: unit,
Contact : contacts.size() > index ? contacts[index] : '',
Location : locations.size() > index ? locations[index] : '',
]
}
return new JsonBuilder(result).toPrettyString()
}
I've omitted the logic of getting string from the message and packaging transformed JSON into message.
I hope it will help you to move forward. Please let me know if you need further assistance here.
You can use the built-in Groovy facilities, like transpose():
import groovy.json.*
def json = new JsonSlurper().parseText '''{ "id":"1222", "storageNode": { "uuid": "22255566336", "properties": {
"BuinessUnit": [ "Light", "Fan", "Watch" ],
"Contact": [ "abc#gmail.com", "fhh#gmail.com" ],
"Location": [ "Banglore", "Surat", "Pune" ] } } }'''
def names = json.storageNode.properties*.key
def values = json.storageNode.properties*.value
int maxSize = values*.size().max()
// pad lists with trainiling spaces
values.each{ v -> ( maxSize - v.size() ).times{ v << '' } }
def result = values.transpose().collect{ tuple -> [ names, tuple ].transpose().collectEntries{ it } }
assert result.toString() == '[[BuinessUnit:Light, Contact:abc#gmail.com, Location:Banglore], [BuinessUnit:Fan, Contact:fhh#gmail.com, Location:Surat], [BuinessUnit:Watch, Contact:, Location:Pune]]'
This piece of code can process everything under storageNode.properties.

How to search a JSON hash using a Regex?

I'm using an API to return all Macros to me, I am trying to return all the "macros" where "actions" contains a "value" matching my Regexp Pattern which I will link below.
I've tried below and other methods, but it returns me nil for present values. Any tips appreciated
macros["value"].select { |m| m['key'] == 'value' }.first['/^DE([0-9a-zA-Z]\s?){20}$/gm']
API result snippet:
jsObj
=> {"macros"=>
[{"url"=>"https://s/1900002708354.json",
"id"=>1900002708354,
"title"=>"Append Signature",
"active"=>true,
"updated_at"=>"2021-10-22T14:11:15Z",
"created_at"=>"2021-10-22T14:11:15Z",
"position"=>10001,
"description"=>"This macro appends a signature to the message ",
"actions"=>[{"field"=>"comment_value_html", "value"=>"<p>Mit besten Grüßen,</p><p>{{current_user.name}} [{{ticket.account}}] <br></p><p><br></p><p>{{dc.signature_email}}<br></p><p><br></p>"}],
"restriction"=>nil},
{"url"=>"949.json",
"id"=>59071949,
"title"=>"information",
"description"=>nil,
"actions"=>[{"field"=>"priority", "value"=>"low"}, {"field"=>"comment_value", "value"=>"DE89370400440532013000" "DE89 3704
0044 0532 0130 00"
"}],
"restriction"=>nil},
Desired Result:
{
"macros": [
{
"url": "x.json",
"id": 1900002708354,
"actions": [
{
"field": "comment_value_html",
"value": "DE89 3704 0044 0532 0130 00"
}
],
"restriction": null
},
{
"url": "x.json",
"id": 59071949,
"actions": [
{
"field": "priority",
"value": "low"
},
{
"field": "comment_value",
"value": "DE89 3704 0044 0532 0130 00
"
}
],
"restriction": null
},
Given that macros is the JSON object containing the macros data, you can use
macros.select { |m| m["actions"].any? { |w| /\ADE(?:[0-9a-zA-Z]\s?){20}\z/.match?(w["value"]) } }
Here is a Ruby demo:
require 'json'
j = <<-DATA
{
"macros": [
{
"url": "x.json",
"id": 1900002708354,
"actions": [
{
"field": "comment_value_html",
"value": "DE11111111111222222220"
}
],
"restriction": null
},
{
"url": "x.json",
"id": 59071949,
"actions": [
{
"field": "priority",
"value": "low"
},
{
"field": "comment_value",
"value": "DE12345678901234567890"
}
],
"restriction": null
}
]}
DATA
jsObj = JSON.parse(j)
macros = jsObj['macros']
puts jsObj['macros'].select { |m| m["actions"].any? { |w| /\ADE(?:[0-9a-zA-Z]\s?){20}\z/.match?(w["value"]) } }
Output:
{"url"=>"x.json", "id"=>1900002708354, "actions"=>[{"field"=>"comment_value_html", "value"=>"DE11111111111222222220"}], "restriction"=>nil}
{"url"=>"x.json", "id"=>59071949, "actions"=>[{"field"=>"priority", "value"=>"low"}, {"field"=>"comment_value", "value"=>"DE12345678901234567890"}], "restriction"=>nil}
The .select { |m| m["actions"].any? { |w| /\ADE(?:[0-9a-zA-Z]\s?){20}\z/.match?(w["value"]) } } main part gets all actions nodes that contain an array with a value key whose value matches the regex given.

How to extract all keys / values from JSON, using Python

I need to extract the "string-value" and "value" from the "contains" element in the following JSON snippet:
{
"pricing-model": {
"qualifier": {
"and": {
"all": {
"equals": {
"constant": {
"string-value": "AFN",
"data-type": "java.lang.String"
},
"value": "item.priceInfo.currencyCode"
},
"collection-name": "items",
"element-name": "item"
},
"or": {
"contains": [
{
"constant": {
"string-value": "AF",
"data-type": "java.lang.String"
},
"value": "locale.country"
},
{
"constant": {
"string-value": "IR",
"data-type": "java.lang.String"
},
"value": "locale.country"
}
]
}
}
}
}
My current approach is too simple example below only extracts one string-value, when I need to extract both:
with open('my.json') as f:
data = json.load(f)
my_dict={}
for item in data:
value = item.get('pricing-model').get('qualifier').get('and').get('or')\
.get('constant')[0].get('string-value')
If you're unsure the keys exist, you can chain dict.get() with an empty dict as a default. That way .get() will always be a valid method of the result.
items = data.get('pricing-model', {}).get('qualifier', {}).get('and', {}).get('or', {}).get('contains', [])
values = [item.get('constant', {}).get('string-value') for item in items if item]
print(values) # ['AF', 'IR']
If you're certain that the keys will exist you can just access them directly as you would a standard dict.
items = data['pricing-model']['qualifier']['and']['or']['contains']
values = [item['constant']['string-value'] for item in items]
print(values)

Generate json dump with dictionaries separated in commas

I'm in the process of generating a json file from python, however, i'd like separate the dictionaries via a comma after each loop, here's a portion of the code:
listA = [computer1,computer2]
listB = [computertype1,computertype2]
for computer, item in zip(listA,listB):
mydict = {
"Computertype": "somevalue",
"computer": [
computer
],
"targert": {
"item": item
}
},
The desired output should be:
[
{
"Computertype": "somevalue",
"computer": [
computer1
],
"targert": {
"item": computertype1
}
},
{
"Computertype": "somevalue",
"computer": [
computer2
],
"targert": {
"item": computertype2
}
}
]
So basically a comma right after the first end brace of the first loop: }, and all in one list, one bracket on top and one closing bracket at the bottom.
When running the code, it doesn't show the comma after each brace in the loop, and it does input each loop within a list automatically, any suggestions ?
What I get:
[ {
"Computertype": "somevalue",
"computer": [
computer1
],
"targert": {
"item": computertype1
}
}
]
[ {
"Computertype": "somevalue",
"computer": [
computer2
],
"targert": {
"item": computertype2
}
}]
I think this is what your looking for:
listA = [computer1,computer2]
listB = [computertype1,computertype2]
mylist = []
for computer, item in zip(listA,listB):
mydict = {
"Computertype": "somevalue",
"computer": [
computer
],
"target": {
"item": item
}
}
mylist.append(mydict)
print(mylist)