Json unmarshal: 强制转换为字符串

huangapple go评论91阅读模式
英文:

Json unmarshal: force into string

问题

我有以下的Go结构体,我想要将一些Json数据解析到其中。除了Values这个类型为map[string]string的map之外,其他都可以正常工作。

type Data struct {
    Id     int               `json:"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",不是一个选项。

英文:

I have the following Go struct into which I want to unmarshal some Json data. It works perfectly except for the Values map, which is of type map[string]string.

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

My Json data (which I can't change the format of), has the following structure and sample data:

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

Unmarshalling the json data fails because Go can't unmarshal the value 7 into a string.

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

Is there a way to implicitly convert the Json values into a string, regardless of the perceived type?
Changing the Json data to format the value as a string, ie key2: "7" is not an option.

答案1

得分: 3

由于JSON中可以包含整数或字符串,如果您使用接口会更好。

类似这样:

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

这样应该可以解决问题。

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

英文:

Since you can have an integer or a string in the json, it would be better if you use an interface.

Something like this:

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

This should do the trick.

Sample code for reference:
https://play.golang.org/p/PjxWeLTwsCC

答案2

得分: 2

你可以创建自己的字符串类型,并实现UnmarshalJSON函数。

type MadString string

type Data struct {
    Id          int                  `json:"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

英文:

You can create own string type and implement UnmarshalJSON function to it.

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
}

Sample: https://play.golang.org/p/PsJRsvQJPMZ

答案3

得分: 1

你可以使用接口和类型断言。

package main

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

func main() {
	type Data struct {
		Id     int                        `json:"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

英文:

You can use interface and type assertions

<pre>
package main

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

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

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

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

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

}

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

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

return str

}
</pre>

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

答案4

得分: 1

为了总结提供的答案,基本上有三种方法:

  1. 使用自定义类型值的映射,该类型“包装”了string并实现了encoding/json.Unmarshaler接口,以便在解析JSON文档中的值时可以灵活处理。

  2. 将子文档解组为map[string]interface{}类型的映射,然后在每次访问该值时使用类型切换,或者从结果映射中填充其他数据类型(在您的情况下可能是map[string]string),无论哪种情况都将使用类型切换。

  3. 使用较低级别的JSON解码工具——encoding/json.Decoder——并在解码阶段决定如何解码特定值,并生成一个string,无论被解析的值实际上是哪种类型。

英文:

To round up the provided answers, there exist basically three approaches:

  1. Use a map of custom-typed values, whose type "wraps" string and implements an encoding/json.Unmarshaler interface to be creative about how it parses the values from the JSON document.

  2. Unmarshal the subdocument into a map of type map[string]interface{} and then either use type-switching on the value on each access to such value or populate some other data type — supposedly map[string]string in your case — from the resulting map (you'll be using type-switching in either case).

  3. Use a lower-level JSON decoding facilities — encoding/json.Decoder — and make decision about how to decode a particular value at decoding phase, and produce a string no matter which type the value being parsed really is.

答案5

得分: 0

你可以在地图值上使用空接口(empty interface)。然后使用Sprintf将其转换为字符串。以下是一个示例,展示了如何解组你的数据:

package main

import (
	"encoding/json"
	"fmt"
)

type Data struct {
	Id     int                    `json:"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)
}

希望对你有帮助!

英文:

You can use the empty interface on your maps values. An then convert it to a string with Sprintf. Here is an example of how you could unmarshal your data:

package main

import (
	&quot;encoding/json&quot;
	&quot;fmt&quot;
)

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

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

	fmt.Println(d)

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

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

huangapple
  • 本文由 发表于 2021年11月15日 16:55:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/69971668.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定