map[string] struct inside struct - json

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.

Related

Go unmarshal JSON with unknown field name but known struct

I retrieve a acme.json from traefik tls where traefik stores ssl/tls certificate information.
Now I want to unmarshal with golang the acme.json into my go struct "Traefik". But I don't know how to handle dynamic/unknown json field names because certificateresolver1 and certificateresolver2 are names I don't know at compile time. These names should be dynamic configured in go.
I know the structure of the json (it is always the same) but not know the field name of the certificateresolver.
Does anyone know the best way to do this?
Traefik acme.json
{
"certificateresolver1": {
"Account": {
"Email": "email#example.com",
"Registration": {
"body": {
"status": "valid",
"contact": [
"mailto:email#example.com"
]
},
"uri": "https://acme-v02.api.letsencrypt.org/acme/acct/124448363"
},
"PrivateKey": "PRIVATEKEY",
"KeyType": "4096"
},
"Certificates": [
{
"domain": {
"main": "example.com",
"sans": [
"test.example.com"
]
},
"certificate": "CERTIFICATE",
"key": "KEY",
"Store": "default"
},
{
"domain": {
"main": "example.org"
},
"certificate": "CERTIFICATE",
"key": "KEY",
"Store": "default"
}
]
},
"certificateresolver2": {
"Account": {
"Email": "email#example.com",
"Registration": {
"body": {
"status": "valid",
"contact": [
"mailto:email#example.com"
]
},
"uri": "https://acme-v02.api.letsencrypt.org/acme/acct/126945414"
},
"PrivateKey": "PRIVATEKEY",
"KeyType": "4096"
},
"Certificates": [
{
"domain": {
"main": "example.net"
},
"certificate": "CERTIFICATE",
"key": "KEY",
"Store": "default"
}
]
}
}
Go struct for acme.json
type Traefik struct {
Provider []struct {
Account struct {
Email string `json:"Email"`
Registration struct {
Body struct {
Status string `json:"status"`
Contact []string `json:"contact"`
} `json:"body"`
URI string `json:"uri"`
} `json:"Registration"`
PrivateKey string `json:"PrivateKey"`
KeyType string `json:"KeyType"`
} `json:"Account"`
Certificates []struct {
Domain struct {
Main string `json:"main"`
Sans []string `json:"sans"`
} `json:"domain"`
Certificate string `json:"certificate"`
Key string `json:"key"`
Store string `json:"Store"`
} `json:"Certificates"`
} `json:"certificateresolver"` <-- What to write there? It should fit for certificateresolver1 and certificateresolver2
}
I think something like this will help you:
type ProviderMdl map[string]Provider
type Provider struct {
Account struct {
Email string `json:"Email"`
Registration struct {
Body struct {
Status string `json:"status"`
Contact []string `json:"contact"`
} `json:"body"`
URI string `json:"uri"`
} `json:"Registration"`
PrivateKey string `json:"PrivateKey"`
KeyType string `json:"KeyType"`
} `json:"Account"`
Certificates []struct {
Domain struct {
Main string `json:"main"`
Sans []string `json:"sans"`
} `json:"domain"`
Certificate string `json:"certificate"`
Key string `json:"key"`
Store string `json:"Store"`
} `json:"Certificates"`
}
So you could work with this data in this way:
bres := new(ProviderMdl)
if err := json.Unmarshal(data, bres); err != nil {
panic(err)
}
// fmt.Printf("%+v - \n", bres)
for key, value := range *bres {
fmt.Printf("%v - %v\n", key, value)
}
Full example here

Fill struct dynamically from parameters

I've the following struct which works as expected Im getting
data and I was able
type Service struct {
Resources []struct {
Model struct {
Name string `json:"name"`
Credentials struct {
path string `json:"path"`
Vts struct {
user string `json:"user"`
id string `json:"id"`
address string `json:"address"`
} `json:"vts"`
} `json:"credentials"`
} `json:"model"`
} `json:"resources"`
}
service:= Service{}
err := json.Unmarshal([]byte(data), &service
The data is like following,
service1
{
"resources": [
"model": {
"name": "cred",
"credentials": {
"path": "path in fs",
"vts": {
"user": "stephane",
"id": "123",
"address": "some address"
}
},
}
},
Now some services providing additional data under vts for example now we have 3 fields (user/id/address) but some services (service1) can provides additional data like email, secondName ,etc . but the big problem here is that
I need to get it from parameters since (service 2) education, salary etc
Service2
{
"resources": [
"model": {
"name": "cred",
"credentials": {
"path": "path in fs",
"vts": {
"user": "stephane",
"id": "123",
"address": "some address",
"email" : "some email",
"secondName" : "secondName"
}
},
}
},
service N
{
"resources": [
"model": {
"name": "cred",
"credentials": {
"path": "path in fs",
"vts": {
"user": "stephane",
"id": "123",
"address": "some address",
"salary" : "1000000"
}
},
}
},
Of course If I know in advance the fields I can put them all in the struct and use omitempty but I dont know, I just get it as parameter to the function (the new properties names) , some service can provide 10 more fields in this struct (which I should get the properties name of them as args[]to the functions) but I don't know them in advance, this should be dynamic somehow ....is there a nice way to handle it in Golang ?
If you don't know the fields in advance, then don't use a struct but something that is also "dynamic": a map.
type Service struct {
Resources []struct {
Model struct {
Name string `json:"name"`
Credentials struct {
Path string `json:"path"`
Vts map[string]interface{} `json:"vts"`
} `json:"credentials"`
} `json:"model"`
} `json:"resources"`
}
map[sting]interface{} can hold values of any type. If you know all fields will hold a string value, you may also use a map[string]string so it will be easier to work with it.
Example with input JSON:
{
"resources": [
{
"model": {
"name": "cred",
"credentials": {
"path": "path in fs",
"vts": {
"user": "stephane",
"id": "123",
"address": "some address",
"dyn1": "d1value",
"dyn2": "d2value"
}
}
}
}
]
}
Testing it:
service := Service{}
err := json.Unmarshal([]byte(data), &service)
fmt.Printf("%q %v", service, err)
Output (try it on the Go Playground):
{[{{"cred" {"path in fs" map["dyn2":"d2value" "user":"stephane" "id":"123"
"address":"some address" "dyn1":"d1value"]}}}]} <nil>
Now if you want to collect values from the Vts map for a set of keys, this is how you can do it:
args := []string{"dyn1", "dyn2"}
values := make([]interface{}, len(args))
for i, arg := range args {
values[i] = service.Resources[0].Model.Credentials.Vts[arg]
}
fmt.Println(values)
Output of the above will be (try it on the Go Playground):
[d1value d2value]

How to create correct struct for json

how to parse json correctly I have the following json file
{
"hello": {
"title": "Golang",
"story": [
"Go lang story",
"Channel story"
],
"options": [
{
"text": "That story",
"arc": "west"
},
{
"text": "Gee",
"arc": "east"
}
]
},
"world": {
"title": "Visiting",
"story": [
"Boo",
"Doo",
"Moo",
"Qoo"
],
"options": [
{
"text": "weird",
"arc": "west"
},
{
"text": "funny",
"arc": "north"
}
]
}
}
I've created these structs for the inner part
type chapter struct{
Title string `json:"title"`
Story []string `json:"story"`
Option []option `json:"options"`
}
type option struct {
Text string `json:"text"`
Arc string `json:"arc"`
}
but I don't know how to parse wrappers like "hello" and "world"
All you need to do structuring the root map.
{
"hello":{},
"world":{}
}
Here the hello and world is also inside a map. So you need to structure them too.
var root map[string]chapter
json.Unmarshal(JSONDATA,&root)
Playground example: https://play.golang.org/p/VZ9Bn215dDW

Parse JSON array in Typescript

i have a JSON response from remote server in this way:
{
"string": [
{
"id": 223,
"name": "String",
"sug": "string",
"description": "string",
"jId": 530,
"pcs": [{
"id": 24723,
"name": "String",
"sug": "string"
}]
}, {
"id": 247944,
"name": "String",
"sug": "string",
"description": "string",
"jlId": 531,
"pcs": [{
"id": 24744,
"name": "String",
"sug": "string"
}]
}
]
}
In order to parse the response, to list out the "name" & "description", i have written this code out:
interface MyObj {
name: string
desc: string
}
let obj: MyObj = JSON.parse(data.toString());
My question is how do i obtain the name and description into a list that can be displayed.
You gave incorrect type to your parsed data. Should be something like this:
interface MyObj {
name: string
description: string
}
let obj: { string: MyObj[] } = JSON.parse(data.toString());
So it's not MyObj, it's object with property string containing array of MyObj. Than you can access this data like this:
console.log(obj.string[0].name, obj.string[0].description);
Instead of using anonymous type, you can also define interface for it:
interface MyRootObj {
string: MyObj[];
}
let obj: MyRootObj = JSON.parse(data.toString());

json request into string

I have the following json structure:
{
"data": [
{
"number": 123,
"animal": "mush"
},
{
"number": "123",
"animal": ""
}
],
"animal_id": 1
}
How can I save it as a string?
It varies by language, but in JavaScript (which might be likely used in your case), JSON.stringify does this job.