Json unmarshal:强制转换为字符串

Json unmarshal: force into string

我有以下 Go 结构,我想将一些 Json 数据解组到其中。除了类型为 map[string]string.

Values 地图外,它工作得很好
type Data struct {
    Id          int               `jons:"id"`
    Values      map[string]string `json:"values"`

我的 Json 数据(我无法更改其格式)具有以下结构和样本数据:

{
   id: 1,
   values: {
      key1: "a string value",
      key2: 7
   }
}

解组 json 数据失败,因为 Go 无法将值 7 解组为字符串。

json: cannot unmarshal number into Go struct field Data.Values of type string

有没有办法将 Json 值隐式转换为字符串,而不考虑感知类型? 更改 Json 数据以将值格式化为字符串,即 key2: "7" 不是一个选项。

既然json里面可以有整数或者字符串,最好是使用接口

像这样:

type Data struct {
    Id          int                       `jons:"id"`
    Values      map[string]interface{}    `json:"values"`
}

这应该可以解决问题。

示例代码供参考: https://play.golang.org/p/PjxWeLTwsCC

您可以使用接口和类型断言

package main

import (
    "encoding/json"
    "fmt"
    "strconv"
)

func main() {
    type Data struct {
        Id          int               `jons:"id"`
        Values      map[string]interface{} `json:"values"`
    }

    jsonData := []byte(`{"id": 1, "values": {"key1": "a string value", "key2": 7}}`)

    data := new(Data)
    err := json.Unmarshal(jsonData, &data)
    if err != nil {
        fmt.Println(err)
    }

    for _, value := range data.Values {
        fmt.Printf("%T\n", ToString(value))
    }
}

func ToString(value interface{}) string {
    str := ""

    switch value.(type) {
    case float64:
        str = strconv.FormatFloat(value.(float64), 'f', 0, 64)
    case int64:
        str = strconv.FormatInt(value.(int64), 10)
    case int:
        str = strconv.Itoa(value.(int))
    case string:
        str = value.(string)
    }

    return str
}

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

您可以创建自己的字符串类型并为其实现 UnmarshalJSON 函数。

type MadSrting string

type Data struct {
    Id          int                  `jons:"id"`
    Values      map[string]MadString `json:"values"`
}

func (mad *MadString) UnmarshalJSON(data []byte) error {
    if n := len(data); n > 1 && data[0] == '"' && data[n-1] == '"' {
        return json.Unmarshal(data, (*string)(mad))
    }   
    
    *mad = MadString(data)

    return nil
}

样本:https://play.golang.org/p/PsJRsvQJPMZ

您可以在地图值上使用空界面。然后使用 Sprintf 将其转换为字符串。以下是如何解组数据的示例:

package main

import (
    "encoding/json"
    "fmt"
)

type Data struct {
    Id     int                    `jons:"id"`
    Values map[string]interface{} `json:"values"`
}

func main() {
    data := `
{
    "id": 1,
    "values": {
        "key1": "a string value",
        "key2": 7
    }
}
`
    var d Data
    err := json.Unmarshal([]byte(data), &d)
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println(d)

    s1 := fmt.Sprintf("%v", d.Values["key1"])
    fmt.Println("key1", s1)

    s2 := fmt.Sprintf("%v",  d.Values["key2"])
    fmt.Println("key2", s2)
}

要汇总提供的答案,基本上存在三种方法:

  1. 使用自定义类型值的映射,其类型“包装”string 并实现一个 encoding/json.Unmarshaler 接口以创造性地解析来自 JSON 文件.

  2. 将子文档解组为类型 map[string]interface{} 的映射,然后在每次访问此类值时对值使用类型切换或填充其他一些数据类型 — 据推测 map[string]string 在你的例子中——来自 生成的地图(在任何一种情况下你都将使用类型切换)。

  3. 使用较低级别的 JSON 解码工具 — encoding/json.Decoder — 并在解码阶段决定如何解码特定值,并生成 string 不管被解析的值是什么类型。