首页 > 解决方案 > 如何将相同值编组和解组为具有不同类型的结构?

问题描述

我正在调用一个外部 API,我将其解组为一个结构。

在响应中,大多数字段都是整数,但由于它是 json,因此有几种极端情况可以返回字符串但仍然是有效/有用的信息:“NaN”、“N/A”

我的结构是这样的:

type Example struct {
  Field1 *int64 `json:"field_1,omitempty"`
  Field2 *int64 `json:"field_2,omitempty"`
  Field3 *int64 `json:"field_3,omitempty"`
}

我们有几个要求:

为了做到这一点,我试图用 JSON 替换“NaN”null

 b = bytes.Replace(b, []byte("NaN"), []byte("null"), -1) ` 

但它不起作用,因为"null"不等于null,这是第一个问题。

第二个问题是 omitempty 在重新编组时也不区分 nil、0 和空值。

所以重新编组也失败了。我知道这是一个正在修复的“常见”问题,但现在有解决方法吗?

因为如果我为“ N/A ”和“NaN”传递 nil 并使用 omitempty 它将删除它们。如果我通过 0 它将没有意义(商业明智,因为 0 具有“未初始化”以外的含义)并且如果我删除 Omitempty 它将每次都编组整个结构(大量不必要的数据)并且无法区分 nil ( NA / NaN ) 和 nil ( 无值 )。

最后一个选项是像这样构建自定义类型和编组/解组器:

type JSONint64 struct {
  value *int64
  error string
}

但这需要我每次都检查我的 json 响应中的每个数字,而实际上 NaN 和 N/A 是非常罕见的情况,并在前端增加了“复杂性”。

我假设这是一个常见问题,因为 JSON 是无类型的,这通常是如何解决的?

标签: jsongomarshallingunmarshalling

解决方案


我会解组一个map[string]interface{}值,然后使用反射或类型断言来确定值的数据类型。

Unmarshal 将其中一项存储在接口值中:

bool, for JSON booleans
float64, for JSON numbers
string, for JSON strings
[]interface{}, for JSON arrays
map[string]interface{}, for JSON objects
nil for JSON null

像这样:

package main

import (
    "encoding/json"
    "fmt"
)

type Example struct {
    Field1 *int64 `json:"field_1,omitempty"`
    Field2 *int64 `json:"field_2,omitempty"`
    Field3 *int64 `json:"field_3,omitempty"`
}

func typeof(v interface{}) string {
    switch v.(type) {
    case float64:
        return "float64"
    case string:
        return "string"
    default:
        return "unknown"
    }
}

func main() {
    d := []byte(`{"field_1": "n/a", "field_2": 2 }"`)
    e := make(map[string]interface{})
    err := json.Unmarshal(d, &e)
    if err != nil {
        fmt.Printf("%v\n", err)
    }
    fmt.Printf("%+v\n", e)
    example := Example{}
    for k, v := range e {
        switch typeof(v) {
        case "float64":
            val := int64(v.(float64))
            switch k {
            case "field_1":
                example.Field1 = &val
            case "field_2":
                example.Field2 = &val
            case "field_3":
                example.Field3 = &val
            default:
                fmt.Printf("Unexpected field: %v\n", k)
            }

        default:
            // display error
        }
    }
    fmt.Printf("Example field_2: %d\n", *example.Field2)

}

推荐阅读