Reputation: 25
In my application, I get some unspecified json string and want to loop through the values of the json string. I use the function json.Unmarshal() to get an objectlist from the json values. That works fine. But unfortnely I get an random list of the json values and not the orginal order of them. I use this example code:
package main
import (
"encoding/json"
"fmt"
)
func main() {
jsonstr := `{
"@odata.context": "http://services.odata.org/V4/TripPinService/$metadata#People/$entity",
"@odata.id": "http://services.odata.org/V4/TripPinService/People('russellwhyte')",
"@odata.etag": "W/\"08D956FAB7E22152\"",
"@odata.editLink": "http://services.odata.org/V4/TripPinService/People('russellwhyte')",
"UserName": "russellwhyte",
"FirstName": "Russell",
"LastName": "Whyte",
"Gender": "Male",
"Concurrency": 637636457076498770
}`
var result interface{}
if err := json.Unmarshal([]byte(jsonstr), &result); err != nil {
fmt.Println("Can't convert json to object.")
fmt.Println(err.Error())
}
odataobjs := result.(map[string]interface{})
for k, v := range odataobjs {
fmt.Print(fmt.Sprintln(k, v))
}
}
See on go-playground.
This can be some of the result list:
@odata.editLink http://services.odata.org/V4/TripPinService/People('russellwhyte')
UserName russellwhyte
Gender Male
Concurrency 6.376364570764988e+17
@odata.id http://services.odata.org/V4/TripPinService/People('russellwhyte')
@odata.etag W/"08D956FAB7E22152"
LastName Whyte
@odata.context http://services.odata.org/V4/TripPinService/$metadata#People/$entity
FirstName Russell
After first search in www I find out, that a json object is in general an unordered list. Thats okay for me, as long as the list is in original order! I can't build some struct, because the json values are unknown at the runtime!
Have you some ideas for me to get the original value list?
Thanks for this input. I have now the original order. But: How can i find out the type of the values? string, nil int and float are okay and i get this. But how can i find out the arrays in the json file with this solutions? Like the following JSON, he says that Emails is an unknown type. And AddressInfo is not listet? So i can't get the sub elements from it.
Json Input:
{
"@odata.context": "http://services.odata.org/V4/TripPinService/$metadata#People",
"value": [
{
"@odata.id": "http://services.odata.org/V4/TripPinService/People('vincentcalabrese')",
"@odata.etag": "W/\"08D957CD4BA2C90E\"",
"@odata.editLink": "http://services.odata.org/V4/TripPinService/People('vincentcalabrese')",
"UserName": "vincentcalabrese",
"FirstName": "Vincent",
"LastName": "Calabrese",
"Emails": [
"[email protected]",
"[email protected]"
],
"AddressInfo": [
{
"Address": "55 Grizzly Peak Rd.",
"City": {
"CountryRegion": "United States",
"Name": "Butte",
"Region": "MT"
}
}
],
"Gender": "Male",
"Concurrency": 637637361498507534
}
]
}
Example Code:
dec := json.NewDecoder(strings.NewReader(jsonstr))
for dec.More() {
// Read prop name
t, err := dec.Token()
if err != nil {
log.Printf("Err: %v", err)
break
}
var name string
var ok bool
if name, ok = t.(string); !ok {
continue // May be a delimeter
}
// Read value:
t, err = dec.Token()
if err != nil {
log.Printf("Err: %v", err)
break
}
//fmt.Printf("Name: %s, Value: %v\n", name, t)
switch t.(type) {
case nil:
logger.Debug(fmt.Sprintf("%s is nil", name))
case string:
logger.Debug(fmt.Sprintf("%s is string", name))
case []interface{}:
logger.Debug(fmt.Sprintf("%s is array", name))
case map[string]interface{}:
logger.Debug(fmt.Sprintf("%s is map", name))
case int16:
logger.Debug(fmt.Sprintf("%s is int16", name))
case int32:
logger.Debug(fmt.Sprintf("%s is int32", name))
case int64:
logger.Debug(fmt.Sprintf("%s is int64", name))
case float32:
logger.Debug(fmt.Sprintf("%s is float32", name))
case float64:
logger.Debug(fmt.Sprintf("%s is float64", name))
default:
logger.Debug(fmt.Sprintf("%s is unknown type", name))
}
}
Output
@odata.context is string
value is unknown type
@odata.id is string
@odata.etag is string
@odata.editLink is string
UserName is string
FirstName is string
LastName is string
Emails is unknown type
[email protected] is string
You have some ideas for it? Thanks you.
Upvotes: 0
Views: 1299
Reputation: 751
Instead of getting the json as random list ,you can get it in ascending order using sort
package.You need to create a slice of your map keys and use the sort.Strings()
method on it ,then you can iterate your slice and get the data in ascending order.Here is the code for the same:
package main
import (
"encoding/json"
"fmt"
"sort"
)
func main() {
jsonstr := `{
"@odata.context": "http://services.odata.org/V4/TripPinService/$metadata#People/$entity",
"@odata.id": "http://services.odata.org/V4/TripPinService/People('russellwhyte')",
"@odata.etag": "W/\"08D956FAB7E22152\"",
"@odata.editLink": "http://services.odata.org/V4/TripPinService/People('russellwhyte')",
"UserName": "russellwhyte",
"FirstName": "Russell",
"LastName": "Whyte",
"Gender": "Male",
"Concurrency": 637636457076498770
}`
var result interface{}
if err := json.Unmarshal([]byte(jsonstr), &result); err != nil {
fmt.Println("Can't convert json to object.")
fmt.Println(err.Error())
}
odataobjs := result.(map[string]interface{})
keysJson := make([]string, 0, len(odataobjs))
for k := range odataobjs {
keysJson = append(keysJson, k)
}
sort.Strings(keysJson)
for _, k2 := range keysJson {
fmt.Println(k2, odataobjs[k2])
}
}
Output:
@odata.context http://services.odata.org/V4/TripPinService/$metadata#People/$entity
@odata.editLink http://services.odata.org/V4/TripPinService/People('russellwhyte')
@odata.etag W/"08D956FAB7E22152"
@odata.id http://services.odata.org/V4/TripPinService/People('russellwhyte')
Concurrency 6.376364570764988e+17
FirstName Russell
Gender Male
LastName Whyte
UserName russellwhyte
Upvotes: 0
Reputation: 418745
By default the encoding/json
package uses Go maps to unmarshal JSON objects. Go maps are not ordered, see Why can't Go iterate maps in insertion order? and In Golang, why are iterations over maps random?
So if you need the original order, you cannot use maps (implicit or explicit). You may use json.Decoder
and decode the input by tokens, which of course gives you the tokens in the original order.
This is how it could look like to your example:
dec := json.NewDecoder(strings.NewReader(jsonstr))
for dec.More() {
// Read prop name
t, err := dec.Token()
if err != nil {
log.Printf("Err: %v", err)
break
}
var name string
var ok bool
if name, ok = t.(string); !ok {
continue // May be a delimeter
}
// Read value:
t, err = dec.Token()
if err != nil {
log.Printf("Err: %v", err)
break
}
fmt.Printf("Name: %s, Value: %v\n", name, t)
}
This will output (try it on the Go Playground):
Name: @odata.context, Value: http://services.odata.org/V4/TripPinService/$metadata#People/$entity
Name: @odata.id, Value: http://services.odata.org/V4/TripPinService/People('russellwhyte')
Name: @odata.etag, Value: W/"08D956FAB7E22152"
Name: @odata.editLink, Value: http://services.odata.org/V4/TripPinService/People('russellwhyte')
Name: UserName, Value: russellwhyte
Name: FirstName, Value: Russell
Name: LastName, Value: Whyte
Name: Gender, Value: Male
Name: Concurrency, Value: 6.376364570764988e+17
Upvotes: 5