Go:解组多种类型的JSON

时间:2017-05-18 21:20:01

标签: json go struct types unmarshalling

我遇到了将JSON响应解组到结构中的问题。我遇到的问题是邮政编码可以作为字符串或整数返回。如何编写unmarshal方法来检查zip是否为int并强制它将其存储为字符串?

STRUCT:

type CustomerAddress struct {
    Line1            string `json:"line1"`
    City             string `json:"city"`
    State            string `json:"state"`
    Zip              string `json:"zip"`
    IsPrimaryAddress string `json:"isPrimaryAddress"`
}

示例Json:

address": [
  {
    "line1": "555 ADDRESS PLACE",
    "city": "DALLAS",
    "state": "TX",
    "isPrimaryAddress": "Y",
    "zip": 55555
  }
]

解组后,结果应该将zip成功转换为字符串:

address": [
  {
    "line1": "555 ADDRESS PLACE",
    "city": "DALLAS",
    "state": "TX",
    "isPrimaryAddress": "Y",
    "zip": "55555"
  }
]

作为尝试,我尝试使用ZipWrapper。

type CustomerAddress struct {
    Line1            string        `json:"line1"`
    City             string        `json:"city"`
    State            string        `json:"state"`
    Zip              ZipWrapper    `json:"zip"`
    IsPrimaryAddress string        `json:"isPrimaryAddress"`
}

type ZipWrapper struct {
   Zip string
}

func (w *ZipWrapper ) UnmarshalJSON(data []byte) (err error) {

    if zip, err := strconv.Atoi(string(data)); err == nil {
        w.Zip = strconv.Itoa(zip)
        return nil
    }
    return json.Unmarshal(data, &w.Zip)
}

这几乎起作用,除了zip现在是CustomerAddress中的嵌套结构,这不是我想要的:

  address": [
  {
    "line1": "555 ADDRESS PLACE",
    "city": "DALLAS",
    "state": "TX",
    "isPrimaryAddress": "Y",
    "zip": {
      "Zip": "55555"
    }
  }
]

有什么想法吗?我觉得这是一个相对容易的任务,但我是一个完整的Go noob,并且我们完全无法解开Unmarshalling的工作方式。

2 个答案:

答案 0 :(得分:6)

json包提供json.Number类型来执行此操作:

type CustomerAddress struct {
    Line1            string      `json:"line1"`
    City             string      `json:"city"`
    State            string      `json:"state"`
    Zip              json.Number `json:"zip"`
    IsPrimaryAddress string      `json:"isPrimaryAddress"`
}

https://play.golang.org/p/PIKSh2c6Mm

如果您需要在没有嵌套结构的情况下自己执行此操作,则可以使用与json.Number相同的方式声明类型,并将string作为基础类型

type ZipWrapper string

func (w *ZipWrapper) UnmarshalJSON(data []byte) (err error) {
    if len(data) > 1 && data[0] == '"' && data[len(data)-1] == '"' {
        data = data[1 : len(data)-1]
    }

    if _, err := strconv.Atoi(string(data)); err != nil {
        return err
    }
    *w = ZipWrapper(string(data))
    return nil
}

答案 1 :(得分:1)

Jim在另一个关于将ZipWrapper定义为字符串的答案中所说的是,你可以采用你所采用的相同方法,但没有嵌套的结构。

比如,定义像这样的字段:

Zip ZipWrapper `json:"zip"`

但是ZipWrapper的定义如下:

type ZipWrapper string

您的UnmarshalJSON功能可以是这样的:

func (w *ZipWrapper) UnmarshalJSON(data []byte) (err error) {

    if zip, err := strconv.Atoi(string(data)); err == nil {
        str := strconv.Itoa(zip)
        *w = ZipWrapper(str)
        return nil
    }
    var str string
    err = json.Unmarshal(data, &str)
    if err != nil {
       return err
    }
    return json.Unmarshal([]byte(str), w)
}

这是一个有效的游乐场:

https://play.golang.org/p/IlJJRP3x1w