Unmarshal Escaped JSON string [duplicate] - json

This question already has an answer here:
decode json including json encoded strings
(1 answer)
Closed 7 years ago.
I'm trying to Unmarshal a JSON object from an API that has a string inside of the JSON that itself is JSON, but it's escaped as a string. It looks something like this:
{
"duration": "126.61ms",
"startTime": "2016-02-19T20:01:17.884Z",
"total": 123,
"content": [
{
"dateCreated": "2016-02-19T20:01:09.181Z",
"lastUpdated": "2016-02-19T20:01:09.181Z",
"name": "name",
"stats": "{\"id\":545,\"lastUpdated\":\"2015-01-09T19:16:04.535Z\",\"all\":{\"runs\":{\"count\":123}"
}
}
I'm trying to unmarshal that into a struct like this:
type RunStatus struct {
Duration string `json:"duration"`
StartTime time.Time `json:"startTime"`
Total int `json:"total"`
Content []struct {
DateCreated time.Time `json:"dateCreated"`
LastUpdated time.Time `json:"lastUpdated"`
name string `json:"name"`
stats string `json:"stats"`
} `json:"content"`
}
What's the best way to get the escaped JSON object into a stats struct rather than it being in a string?

Do this in two phases. First unmarshal the outer object with the stats field as a string, then unmarshal that string into the stats struct. You could do a custom implementation of UnmarshalJSON so this is obfuscated but either way the only reasonable approach I know of is to do the two separately.

Related

Representing a list of things of different type in JSON and Golang [duplicate]

This question already has answers here:
Is it possible to partially decode and update JSON? (go)
(2 answers)
How to parse a complicated JSON with Go unmarshal?
(3 answers)
golang | unmarshalling arbitrary data
(2 answers)
How to parse JSON in golang without unmarshaling twice
(3 answers)
Decoding generic JSON objects to one of many formats
(1 answer)
Closed 9 months ago.
I'm trying to represent some data in JSON where there is a list of things where each thing has some common features (ex name) and another field who's value can either be a string or an integer. For example:
{
"items": [
{
"name": "thing1",
"type": "string",
"value": "foo"
},
{
"name": "thing2",
"type": "int",
"value": 42
}
]
}
That JSON looks reasonable to me, but trying to create a data structure to deserialize (unmarshal) it into in Golang is proving difficult. I think I could do it in Java with class polymorphism, but in Go I feel trapped. I've tried many things but haven't got it. Ultimately, it comes down to lack of struct type polymorphism.
In Go, I can have a slice (list) of interfaces, but I need actual structs of different types as far as I can tell.
Any suggestions on how to represent this in Golang, and be able to unmarshal into?
Or alternatively, should I structure the JSON itself differently?
Thanks!
You can create a structure like this
type items struct {
name string
type_1 string
value interface{}
}
Interface can hold any data type, also as type is reserved keyword i have used type_1
You can do it in Go 1.18 like this:
type Data struct {
Items []struct {
Name string `json:"name"`
Type string `json:"type"`
Value any `json:"value"`
} `json:"items"`
}
func main() {
data := Data{}
// it's your json bytes
bytesData := []byte()
if err := json.Unmarshal(byteData, &data); err != nil {
log.Fatal(err)
}
}
// use data here
P.S. if you are using older versions use interface{} instead of any.
This data structure properly represents your JSON:
type Data struct {
Items []struct {
Name string `json:"name"`
Type string `json:"type"`
Value interface{} `json:"value"`
} `json:"items"`
}
Then, you can use json.Unmarshal.
If you use Go 1.18, you can use any alias of interface{}.
In addition, in Go you don't even need a type field. You can use Type assertions to determine the value type.

how to create user defined struct for array of different user defined object in golang

I am trying to parse json object in golang.
Below is the json structure :
{
"properties": {
"alertType": "VM_EICAR",
"resourceIdentifiers": [{
"azureResourceId": "/Microsoft.Compute/virtualMachines/vm1",
"type": "AzureResource"
},
{
"workspaceId": "f419f624-acad-4d89-b86d-f62fa387f019",
"workspaceSubscriptionId": "20ff7fc3-e762-44dd-bd96-b71116dcdc23",
"workspaceResourceGroup": "myRg1",
"agentId": "75724a01-f021-4aa8-9ec2-329792373e6e",
"type": "LogAnalytics"
}
],
"vendorName": "Microsoft",
"status": "New"
}
}
I have below user defined types.
type AzureResourceIdentifier struct {
AzureResourceId string `json:"azureResourceId"`
Type string `json:"type"`
}
type LogAnalyticsIdentifier struct{
AgentId string `json:"agentId"`
Type string `json:"type"`
WorkspaceId string `json:"workspaceId"`
WorkspaceResourceGroup string `json:"workspaceResourceGroup"`
WorkspaceSubscriptionId string `json:"workspaceSubscriptionId"`
}
I have a top level user defined type as properties as below.
it has resourceIdentifiers as array of two other user defined types(defined above),
AzureResourceIdentifier
LogAnalyticsIdentifier
how can I define type of resourceIdentifiers in properties ?
type Properties struct{
alertType string
resourceIdentifiers ??? `
vendorName string `
status string
}
Note: I have a existing connector and we are provisioned to input the struct of the parsing json, we cannot override or add any function.
There are a couple of options here.
[]map[string]interface{}
Using a map will allow any JSON object in the array to be parsed, but burdens you with having to safely extract information after the fact.
[]interface{}
Each is going to be a map under the hood, unless non-JSON object elements can also be in the JSON array. No reason to use it over map[string]interface{} in your case.
A slice of a new struct type which covers all possible fields: []ResourceIdentifier
type ResourceIdentifier struct {
AzureResourceId string `json:"azureResourceId"`
AgentId string `json:"agentId"`
WorkspaceId string `json:"workspaceId"`
WorkspaceResourceGroup string `json:"workspaceResourceGroup"`
WorkspaceSubscriptionId string `json:"workspaceSubscriptionId"`
Type string `json:"type"`
}
This is probably the laziest solution. It allows you to get where you want to quickly, at the cost of wasting some memory and creating a non self-explanatory data design which might cause confusion if later trying to serialize with it again.
A new struct type + custom unmarshalling implementation.
type ResourceIdentifiers struct {
AzureResourceIdentifiers []AzureResourceIdentifier
LogAnalyticsIdentifiers []LogAnalyticsIdentifier
}
Implement json.Unmarshaler to decide which type of struct to construct and in which slice to put it.

Parsing complex JSON, again

I have some JSON that get via an API call and I want to now parse this using JSON, I followed an online tutorial in how to parse JSON using structs, but my actual JSON is a lot more complex than the one they used. Here is an example of the JSON I have:
{
"metadata": {},
"items": [
{
"metadata": {
"name": "run7",
"namespace": "default",
"uid": "e218fcc4",
"creationTimestamp": "2022-01-01T00:00:00Z"
},
"spec": {
"arguments": {}
},
"status": {
"phase": "Succeeded",
"startedAt": "2022-01-01T00:00:00Z",
"finishedAt": "2022-01-01T00:00:00Z"
}
}
]
}
and here is the strucs that I created for it:
type wfSpec struct{
Arguments string
}
type wfStatus struct {
Phase string
StartedAt string
FinishedAt string
}
type wfMetadata struct {
Name string
Namespace string
Uid string
CreationTimestamp string
}
type Metadata []struct {
Data string
}
type Items []struct {
wfMetadata
wfStatus
wfSpec
}
type Workflow struct {
Metadata Metadata
Items Items
}
When I first tried to print a value using fmt.Printf(workflows.Items.wfMetadata.Name) I got the error workflows.Items.Metadata undefined (type Items has no field or method Metadata)so then I tried to just print the whole thing using fmt.Printf(workflows) and I got this error cannot use workflows (type Workflow) as type string in argument to fmt.Printf
The only data I need to parse from the JSON is the
"name": "run7",
"namespace": "default",
"uid": "e218fcc4",
"creationTimestamp": "2022-01-01T00:00:00Z"
First off
The problem I expect you're having is not using the tags. To parse a JSON the names of the structs must match the names in the JSON fields. Read here Golang Marshal
Secondly wfMetadata has a lowecase first letter, meaning it will not be imported.
Thirdly, workflow.metadata and workflow.items[i].spec.arguments is set as a {} and not the emptystring "". I assume they're not supposed to be string. This can be avoided using the open interface{} if you don't know or care, or actually implementing them using the official documentations from the API you're connecting to.
As a note, using []struct seems wrong to me. Instead define it in the usage
Note, by using an IDE like GoLand from jetbrains they first off support converting JSON to a struct by simply pasting the JSON into a .go file. They might be daunting at first but do help a lot, and would do much of this for you in seconds.
Now try this instead, and understand why and how this is better and working.
type Status struct {
Phase string `json:"phase"`
StartedAt string `json:"startedAt"`
FinishedAt string `json:"finishedAt"`
}
type ItemMetadata struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
UID string `json:"uid"`
CreationTimestamp string `json:"creationTimestamp"`
}
type Items struct {
Metadata ItemMetadata `json:"metadata"`
Status Status `json:"status"`
Spec interface{} `json:"spec"`
}
type Workflow struct {
Metadata interface{} `json:"metadata"`
Items []Items `json:"items"`
}
Working example in playground https://go.dev/play/p/d9rT4FZJsGv

Parsing inconsitent json structure

I am using duckling as a tool for date/time and unit extraction. When I send a request like today, I am getting (some fields have been removed for readability):
{"body":"today", "value": {"value":"2019-08-10T00:00:00.000-07:00", "grain":"day", "type":"value"}, "dim":"time"}
Thus, I created these structs:
type DucklingEntry struct {
Body string `json:"body"`
Dim string `json:"dim"`
Value DucklingValue `json:"value"`
}
type DucklingValue struct {
Value string `json:"value"`
Grain string `json:"grain"`
Type string `json:"type"`
}
When I send a request with the text value 6 euro, I am getting:
{"body":"6 euro", "value": {"value":6, "type":"value", "unit":"EUR"}, "dim":"amount-of-money"}
As you can see, the inner value field now contains an integer instead of a string. When I parse the json string using the shown struct, the inner value value will be "".
How can I account for such an inconsistent json response?

Struct for JSON objects in GO [duplicate]

This question already has an answer here:
Strange type definition syntax in Golang (name, then type, then string literal)
(1 answer)
Closed 7 years ago.
I'm learning GO and when defining structs for working with JSON like below.
type List struct {
ID string `datastore:"-"`
Name string
}
I see that there is this text in between ` sign. I have not been able to find an explanation what that signifies.
Things seem to work even without those.
They are struct tags used in Marshal'ing Go struct into JSON. In JSON, unlike Go, fields are in lowercase strings. Therefore, most use cases would be
type List struct {
ID string `json:"id"`
Name string `json:"name"`
}
In JSON
{
"id": "some id",
"name": "some name"
}
See post here