JSON 值的原始顺序

dap*_*aul 0 json go

在我的应用程序中,我得到一些未指定的 json 字符串,并希望循环遍历 json 字符串的值。我使用函数 json.Unmarshal() 从 json 值获取对象列表。效果很好。但不幸的是,我得到了 json 值的随机列表,而不是它们的原始顺序。我使用这个示例代码:

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))
    }
}
Run Code Online (Sandbox Code Playgroud)

请参阅go-playground

这可能是一些结果列表:

@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
Run Code Online (Sandbox Code Playgroud)

在 www 中第一次搜索后,我发现 json 对象通常是一个无序列表。这对我来说没问题,只要列表按原始顺序排列即可!我无法构建一些结构,因为 json 值在运行时是未知的!

您有什么想法可以帮助我获得原始价值清单吗?

感谢您的意见。我现在有原始订单。但是:我如何找出值的类型?string、nil int 和 float 都可以,我明白了。但是我如何使用此解决方案找到 json 文件中的数组呢?就像下面的 JSON 一样,他说Emails是未知类型。而且AddressInfo不是listet?所以我无法从中获取子元素。

json输入:

{
    "@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": [
                "Vincent@example.com",
                "Vincent@contoso.com"
            ],
            "AddressInfo": [
                {
                    "Address": "55 Grizzly Peak Rd.",
                    "City": {
                        "CountryRegion": "United States",
                        "Name": "Butte",
                        "Region": "MT"
                    }
                }
            ],
            "Gender": "Male",
            "Concurrency": 637637361498507534
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

示例代码:

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))
        }
    }
Run Code Online (Sandbox Code Playgroud)

输出

@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
Vincent@example.com is string
Run Code Online (Sandbox Code Playgroud)

你对此有一些想法吗?感谢您。

icz*_*cza 5

默认情况下,该encoding/json包使用 Go 映射来解组 JSON 对象。Go 映射没有排序,请参阅为什么 Go 不能按插入顺序迭代映射?Golang 中,为什么映射的迭代是随机的?

因此,如果您需要原始顺序,则不能使用映射(隐式或显式)。您可以通过令牌使用json.Decoder和解码输入,这当然会按原始顺序为您提供令牌。

这就是您的示例的样子:

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)
}
Run Code Online (Sandbox Code Playgroud)

这将输出(在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
Run Code Online (Sandbox Code Playgroud)