ELM parse nested json - json

I have a json array with multiple comments which can be nested.
exemple:
[
{
"author": "john",
"comment" : ".....",
"reply": "",
},
{
"author": "Paul",
"comment" : ".....",
"reply": [
{
"author": "john",
"comment" : "nested comment",
"reply": [
{
"author": "Paul",
"comment": "second nested comment"
}
]
},
{
"author": "john",
"comment" : "another nested comment",
"reply": ""
}
]
},
{
"author": "Dave",
"comment" : ".....",
"reply": ""
},
]
So it's a list of comment, which every comment can have a reply with an infinite number of reply.
With Json.Decode.list I can decode the first level of comment, but how do I checked if there is some reply and then parse again ?
This is a simplify version of what I'm try to do. I'm actually trying to decode reddit comments. exemple

Elm won't let you create a recursive record type alias, so you'll have to use a union type for Customer. You may also want a convenience function for creating a user so you can use Json.map3.
Your example json has an oddity: Sometimes reply is an empty string and sometimes it's a list. You'll need a special decoder to turn that string into an empty list (assuming an empty list is synonymous with an empty list in this context).
Since you have a recursive type, you need to use lazy for decoding the child comments to avoid a runtime error.
import Html exposing (Html, text)
import Json.Decode as Json exposing (..)
main : Html msg
main =
text <| toString <| decodeString (list commentDecoder) s
type Comment
= Comment
{ author : String
, comment : String
, reply : List Comment
}
newComment : String -> String -> List Comment -> Comment
newComment author comment reply =
Comment
{ author = author
, comment = comment
, reply = reply
}
emptyStringToListDecoder : Decoder (List a)
emptyStringToListDecoder =
string
|> andThen
(\s ->
case s of
"" ->
succeed []
_ ->
fail "Expected an empty string"
)
commentDecoder : Decoder Comment
commentDecoder =
map3 newComment
(field "author" string)
(field "comment" string)
(field "reply" <|
oneOf
[ emptyStringToListDecoder
, list (lazy (\_ -> commentDecoder))
]
)
s =
"""
[{
"author": "john",
"comment": ".....",
"reply": ""
}, {
"author": "Dave",
"comment": ".....",
"reply": ""
}, {
"author": "Paul",
"comment": ".....",
"reply": [{
"author": "john",
"comment": "nested comment",
"reply": [{
"author": "Paul",
"comment": "second nested comment",
"reply": ""
}]
}, {
"author": "john",
"comment": "another nested comment",
"reply": ""
}]
}]
"""
(Your json is also a little off in other ways: There are a few extra commas after the last parts of list and one of the reply fields is missing)

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
}

JSON Parameter Config Groovy Script not working

I am trying to create a "Extended choice config" parameter. I am putting my code in "JSON Parameter Config Groovy Script", but I cannot see it in "build with parameters" screen. I tried with both, regular json and org.boon and got the same [no] results.
the code I used (for org.boon) is the same that can be found on: https://wiki.jenkins.io/pages/viewpage.action?pageId=92736450
Replace your config groovy script with the below code and it will work. The JSON Parameter feature for the Extended Choice Parameter is based on the https://github.com/json-editor/json-editor
The groovy script should return a JSON object that corresponds to the "options" object referred to in json-editor.
UPDATE
Ensure that your Extended name choice parameter "Name" doesnt have space. So in your case change the name from "Testing Json" to "testing_json" or Json (string without space) and it will work.
import net.sf.json.JSONObject
def jsonEditorOptions = JSONObject.fromObject(/{
disable_edit_json: true,
disable_properties: true,
no_additional_properties: true,
disable_collapse: true,
disable_array_add: true,
disable_array_delete: true,
disable_array_reorder: true,
theme: "bootstrap2",
iconlib:"fontawesome4",
schema: {
"type": "object",
"title": "Name",
"properties": {
"first_name": {
"type": "string",
"propertyOrder" : 1
},
"last_name": {
"type": "string",
"propertyOrder" : 2
},
"full_name": {
"type": "string",
"propertyOrder" : 3,
"template": "{{fname}} {{lname}}",
"watch": {
"fname": "first_name",
"lname": "last_name"
}
}
}
},
startval: {
"first_name" : "John",
"last_name" : "Doe",
"full_name" : "John Doe"
}
}/);

TSQL JSON How to add an array to an existing Json Object?

We've got a SQL query where we create a JSON file with for JSON Path.
We want to merge 2 JSON objects into 1 JSON. But we struggle with the code how to accomplish this task.
We tried JSON_MODIFY to merge them together using append. But this did not work for us.
What we'd like to do is this, we have 2 seperate json objects and we want to merge them as one.
Json Object A:
{
"ID" : 0,
"Name" : "a name",
"Description" : "a description"
}
and Json Object B
"Nodes" : [
{
"NodeID" : 10,
"NodeName" : "Node 0"
},
{
"NodeID" : 11,
"NodeName" : "Node 1"
}
]
What we want to have:
{
"ID" : 0,
"Name" : "a name",
"Description" : "a description",
"Nodes" : [
{
"NodeID" : 10,
"NodeName" : "Node 0"
},
{
"NodeID" : 11,
"NodeName" : "Node 1"
}
]
}
Our current SQL Query looks like this:
set #JsonCourse = ( select c.name, c.id, c.description from dbo.courses c where c.id = #id for json path)
set #JsonNodes = ( select n.id, n.name from dbo.nodes n where n.courseId = #id for json path, root('Nodes'))
set #CompleteJson = JSON_MODIFY(#JsonCourse,'append $',JSON_QUERY(#JsonNodes));
print #CompleteJson
But our result is like this:
[
{
"ID" : 0,
"Name" : "a name",
"Description" : "a description"
},
{
"Nodes" : [
{
"NodeID" : 10,
"NodeName" : "Node 0"
},
{
"NodeID" : 11,
"NodeName" : "Node 1"
}
]
}
]
Note: we've used hypothetical data here.
How do we fix this with JSON_MODIFY?
So I'll add another answer, as this is a completely different thing as the first answer:
As I do not have your tables, I'll set the JSON variables to the values you provided
DECLARE #json1 NVARCHAR(MAX)=
N'{
"ID" : 0,
"Name" : "a name",
"Description" : "a description"
}'
DECLARE #json2 NVARCHAR(MAX)= --<-- had to add the surrounding {}, otherwise this was invalid JSON
N'{"Nodes" :
[
{
"NodeID" : 10,
"NodeName" : "Node 0"
},
{
"NodeID" : 11,
"NodeName" : "Node 1"
}
]}';
--We do not need append here.
--We have to tell the engine the name of the new node.
--To avoid repeated key Nodes I read from the #json2 using $.Nodes as path
DECLARE #CompleteJSON NVARCHAR(MAX)=JSON_MODIFY(#Json1,'$.Nodes',JSON_QUERY(#Json2,'$.Nodes'));
PRINT #CompleteJSON;
I hope this is closer to your needs...
Please read about creating a MCVE. This is a stand-alone sample, which makes your issue reproducible, and helps us to provide easy answers...
If I got this correctly there is a 1:n related structure, where each node in your "object A" can have several nodes in "object B".
My following code will simulate this through INFORMATION_SCHEMA. Each table as 1 or many columns.
We solve this with a correlated sub-query. This is the way to create nested JSON arrays:
SELECT TOP 3 t.TABLE_NAME AS NodeName
,t.TABLE_TYPE AS NodeType
,(
SELECT TOP 3 c.COLUMN_NAME AS ColumnName
,c.DATA_TYPE AS ColumnType
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE c.TABLE_CATALOG=t.TABLE_CATALOG
AND c.TABLE_SCHEMA=t.TABLE_SCHEMA
AND c.TABLE_NAME=t.TABLE_NAME
FOR JSON PATH
) AS MyColumns
FROM INFORMATION_SCHEMA.TABLES t
FOR JSON PATH;
The result
[
{
"NodeName": "spt_fallback_db",
"NodeType": "BASE TABLE",
"MyColumns": [
{
"ColumnName": "xserver_name",
"ColumnType": "varchar"
},
{
"ColumnName": "xdttm_ins",
"ColumnType": "datetime"
},
{
"ColumnName": "xdttm_last_ins_upd",
"ColumnType": "datetime"
}
]
},
{
"NodeName": "spt_fallback_dev",
"NodeType": "BASE TABLE",
"MyColumns": [
{
"ColumnName": "xserver_name",
"ColumnType": "varchar"
},
{
"ColumnName": "xdttm_ins",
"ColumnType": "datetime"
},
{
"ColumnName": "xdttm_last_ins_upd",
"ColumnType": "datetime"
}
]
},
{
"NodeName": "spt_fallback_usg",
"NodeType": "BASE TABLE",
"MyColumns": [
{
"ColumnName": "xserver_name",
"ColumnType": "varchar"
},
{
"ColumnName": "xdttm_ins",
"ColumnType": "datetime"
},
{
"ColumnName": "xdttm_last_ins_upd",
"ColumnType": "datetime"
}
]
}
]
As you can see, each table as a nested set of columns, represented through a JSON array.

map[string] struct inside struct

I have a JSON file that looks like this:
{
"jailbreaks": [
{
"jailbroken": false,
"name": "",
"version": "",
"url": "",
"anleitung": [],
"ios": {
"start": "10.2.1"
},
"caveats": "",
"platforms": []
},
{
"jailbroken": true,
"name": "Yalu102",
"version": "beta 6",
"url": "https://domain-dl.tld",
"anleitung": [
{ "blog": "title", "link": "http://domain.tld/" },
{ "blog": "Test", "link": "http://google.at" }
],
"ios": {
"start": "10.2"
},
"caveats": "some text here",
"platforms": [
"Windows",
"OS X",
"Linux"
]
},
And I create the object to work with like this:
type Jailbreak struct {
Jailbroken bool `json:"jailbroken"`
Name string `json:"name"`
Version string `json:"version"`
URL string `json:"url"`
Anleitung map[string]struct {
Name string `json:"blog"`
Link string `json:"link"`
} `json:"anleitung"`
Firmwares struct {
Start string `json:"start"`
End string `json:"end"`
} `json:"ios"`
Platforms []string `json:"platforms"`
Caveats string `json:"caveats"`
}
When I want to build my go program I get an error, that the JSON file cannot be read. But as soon as I delete the map[string]struct I can compile and run the program without any error and everything works fine.
Am I messing around with something or is there an error in my JSON file?
The json provided is not valid (as the array does not have a closing ] and the top level json object lacks another closing }) so let's assume it's like:
{
"jailbreaks": [
{
"jailbroken": false,
"name": "",
"version": "",
"url": "",
"anleitung": [],
"ios": {
"start": "10.2.1",
"end": ""
},
"platforms": [],
"caveats": ""
},
{
"jailbroken": true,
"name": "Yalu102",
"version": "beta 6",
"url": "https://domain-dl.tld",
"anleitung": [
{
"blog": "title",
"link": "http://domain.tld/"
},
{
"blog": "Test",
"link": "http://google.at"
}
],
"ios": {
"start": "10.2",
"end": ""
},
"platforms": [
"Windows",
"OS X",
"Linux"
],
"caveats": "some text here"
}
]
}
The data structure Jailbreaks (first one), marshals-to/unmarshals-from this json properly:
type Jailbreaks struct {
List []Jailbreak `json:"jailbreaks"`
}
type Jailbreak struct {
Jailbroken bool `json:"jailbroken"`
Name string `json:"name"`
Version string `json:"version"`
URL string `json:"url"`
Anleitung []struct {
Name string `json:"blog"`
Link string `json:"link"`
} `json:"anleitung"`
Firmwares struct {
Start string `json:"start"`
End string `json:"end"`
} `json:"ios"`
Platforms []string `json:"platforms"`
Caveats string `json:"caveats"`
}
As you see Anleitung is declared as a slice (not a map).
Use omitempty flag for when your "anleitung" is empty in JSON to be consumed. Beware though, when that is the case, your Jailbreak struct won't have an "anleitung" field.
Change your map's json flag to to;
Anleitung map[string]struct {
Name string `json:"blog"`
Link string `json:"link"`
} `json:"anleitung,omitempty"`
Option 2;
I guess you could also use Anleitung map[string]interface{} but that is better for "holding a map of strings to arbitrary data types". In your case the data is not arbitrary but rather, empty I guess. And looks like that is just temporary.
I'd go for option 1, then I'd check if my struct contains any Anleitung data or not before accessing it.

nested dictionaries converted to json swift

I Have a Dictionary of Dictionaries that I need to convert to Json.
[
Dict1:1,
test: A Value,
NestedDict1:[
city: A City Name,
address: An Address,
NestedDict2: [
1: 1,
39: 2
],
favorite1: 2,
favorite3: Chocolate
]
]
When I use the
NSJSONSerialization.dataWithJSONObject(myJsonDict, options: NSJSONWritingOptions.PrettyPrinted, error: nil)
it only encodes the outer most dictionary. So my output looks something like this:
{
"Dict1":"1",
"test": "A Value",
"NestedDict1":"[
city: A City Name,
address: An Address,
NestedDict2: [
1: 1,
39: 2
],
favorite1: 2,
favorite3: Chocolate
]"
}
How do I JSON the inner dictionaries as well?
Swift 3
let myJsonDict : [String: Any] = [
"Dict1": "1",
"test": "A Value",
"NestedDict1":[
"city": "A City Name",
"address": "An Address",
"NestedDict2": [
"1": "1",
"39": "2"
],
"favorite1": "2",
"favorite3": "Chocolate"
]
]
let jsonObject = try? JSONSerialization.data(withJSONObject: myJsonDict, options: [])
if let jsonString = String(data: jsonObject!, encoding: .utf8) {
print(jsonString)
}
Output
{"test":"A Value","Dict1":"1","NestedDict1":{"favorite1":2,"city":"A
City
Name","NestedDict2":{"1":"1","39":"2"},"favorite3":"Chocolate","address":"An
Address"}}
I think the problem is more with your representation of the data.
If you can get away with changing all the keys to Strings, then it can be a dictionary, since Strings conform to Hashable. Otherwise, it would be defined as Any, and can't really be a Dictionary key.
Doing this allows the following:
let myJsonDict : [String:AnyObject] = [
"value": 1123,
"test": "some string",
"NestedDict1": [
"city": "A City Name",
"address": "An Address",
"NestedDict2": [
"1": 1,
"39": 2
],
"favorite1": 2,
"favorite3": "Chocolate"
]
]
var jsonObject = NSJSONSerialization.dataWithJSONObject(myJsonDict, options: NSJSONWritingOptions.PrettyPrinted, error: nil)
println(NSString(data: jsonObject!, encoding: NSUTF8StringEncoding)!)
which gives the following output:
{
"test" : "some string",
"NestedDict1" : {
"city" : "A City Name",
"address" : "An Address",
"favorite3" : "Chocolate",
"NestedDict2" : {
"1" : 1,
"39" : 2
},
"favorite1" : 2
},
"value" : 1123
}