将包含变量类型的 JSON 转换为字符串。

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

Convert json with variable types to strings

问题

我正在从API响应中读取JSON,并遇到一个问题,即JSON值内部有多种数据类型(字符串、null、布尔值)。此外,一些键的值可以是字符串或null,这使得将数据读入类型更加困难。我想将所有内容转换为字符串以便于处理。我根据搜索到的其他示例创建了一个基于类型的转换开关。我想知道这是否是最简单的方法,或者是否有更简单的方法。

package main

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

func main() {

	json_byte := []byte(`{"response":[{"t_int":1, "t_bool": true,  "t_null_or_string": null}, {"t_int":2, "t_bool": false, "t_null_or_string": "string1"}]}`)

	//unmarshal the json to data structure using interface for variable data types
	data_json := make(map[string][]map[string]interface{}) //create a structure to hold unmarshalled json
	if err := json.Unmarshal(json_byte, &data_json); err != nil {
		panic(err)
	}
	fmt.Println("json_data: ", data_json)

	//Iterate over data structure and convert Bool, Int, and Null types to string
	var v_conv string                               // temporary holding for converted string values
	data_map := make(map[string]string)             // temporary holding for converted maps
	data_final := make([]map[string]string, 0, 100) // final holding for data converted to strings

	for _, v := range data_json { //v is the value of the "response": key which is a slice of maps
		for _, v2 := range v { //v2 is one of the maps in the slice of maps
			for k3, v3 := range v2 { //k3 and v3 are the keys and values inside the map
				fmt.Println("k3: ", k3, "v3: ", v3)
				switch v_type := v3.(type) {
				case nil:
					v_conv = ""
				case bool:
					v_conv = strconv.FormatBool(v3.(bool))
				case int:
					v_conv = strconv.Itoa(v3.(int))
				case string:
					v_conv = v3.(string)
				case float64:
					v_conv = strconv.FormatFloat(v3.(float64), 'f', 0, 64)
				default:
					fmt.Println("vtype unknown: ", v_type) //have to use v_type since it is declared
					v_conv = ""
				}
				data_map[k3] = v_conv //append a new map key/value pair both as strings
				fmt.Println("data_map: ", data_map)
			}
			data_final = append(data_final, data_map) // after each cycle through the loop append the map to the new list
			fmt.Println("data_final: ", data_final)
		}
	}
}

期望的最终格式是一个映射的切片:

[{
   "t_int":  "1",
   "t_bool": "true",
   "t_null_string": ""
},
{
   "t_int":  "2",
   "t_bool": "false",
   "t_null_string": "string1"
}]
英文:

I am reading in json from an API response and I ran into an issue in that there are multiple data types inside the json values (strings, null, bool). In addition, some keys have values which can be either a string or null which makes reading the data into types more difficult. I want to convert everything to strings for ease of handling. I created a type switch based on googling other examples. I am wondering if this is the easiest way to do this or if I am missing a simpler approach.

  package main
import (
"encoding/json"
"fmt"
"strconv"
)
func main() {
json_byte := []byte(`{"response":[{"t_int":1, "t_bool": true,  "t_null_or_string": null}, {"t_int":2, "t_bool": false, "t_null_or_string": "string1"}]}`) 
//unmarshal the json to data structure using interface for variable data types
data_json := make(map[string][]map[string]interface{}) //create a structure to hold unmarshalled json
if err := json.Unmarshal(json_byte, &data_json); err != nil {
panic(err)
}
fmt.Println("json_data: ", data_json)
//Iterate over data structure and convert Bool, Int, and Null types to string
var v_conv string                               // temporary holding for converted string values
data_map := make(map[string]string)             // temporary holding for converted maps
data_final := make([]map[string]string, 0, 100) // final holding for data converted to strings
for _, v := range data_json { //v is the value of the "response": key which is a slice of maps
for _, v2 := range v { //v2 is one of the maps in the slice of maps
for k3, v3 := range v2 { //k3 and v3 are the keys and values inside the map
fmt.Println("k3: ", k3, "v3: ", v3)
switch v_type := v3.(type) {
case nil:
v_conv = ""
case bool:
v_conv = strconv.FormatBool(v3.(bool))
case int:
v_conv = strconv.Itoa(v3.(int))
case string:
v_conv = v3.(string)
case float64:
v_conv = strconv.FormatFloat(v3.(float64), 'f', 0, 64)
default:
fmt.Println("vtype unknown: ", v_type) //have to use v_type since it is declared
v_conv = ""
}
data_map[k3] = v_conv //append a new map key/value pair both as strings
fmt.Println("data_map: ", data_map)
}
data_final = append(data_final, data_map) // after each cycle through the loop append the map to the new list
fmt.Println("data_final: ", data_final)
}
}
}

Final Format Desired a Slice of Maps
[{
"t_int": "1",
"t_bool": "true",
"t_null_string": ""
},
{
"t_int": "2",
"t_bool": "false",
"t_null_string": "string1"
}]

答案1

得分: 2

对于这个答案,我假设你的示例中的 JSON 是你的 JSON 输入的一部分(或者是示例)。
在这种情况下,你的 JSON 具有特定的结构:你知道哪些属性具有已知的数据类型,也知道哪些属性是动态的。
例如,你可以将你的 JSON 反序列化为类似下面的 ResponseObj:

package main

import (
	"encoding/json"
	"fmt"
)

type ResponseObj struct {
	Response []Item `json:"response"`
}

type Item struct {
	TInt   int         `json:"t_int"`
	TBool  bool        `json:"t_bool"`
	TMixed interface{} `json:"t_null_or_string"`
}

func main() {

	json_byte := []byte(`{"response":[{"t_int":1, "t_bool": true,  "t_null_or_string": null}, {"t_int":2, "t_bool": false, "t_null_or_string": "string1"}]}`)

	data_json := ResponseObj{}
	if err := json.Unmarshal(json_byte, &data_json); err != nil {
		panic(err)
	}
	fmt.Printf("%+v\n", data_json)
}

你的数据将会是这样的:

{
	Response:
	[
		{
			TInt:1
			TBool:true
			TMixed:<nil>
		},
		{
			TInt:2
			TBool:false
			TMixed:string1
		}
	]
}

是的,对于具有混合类型的属性,你将会进行类型断言(或者像你的情况一样与 nil 进行比较,或者两者都有)。

不太可能你的 JSON 是一个完全混乱的、不可预测的类型。很可能,你可以单独提取出一个“核心”结构,并对剩余的混合类型使用 interface{}。

希望对你有所帮助。

英文:

For this answer I'm assuming that JSON in your example is an example of (part of) your JSON input.
In this case, your JSON has a specific structure: you know which attributes are coming with a known data type and also you know which attributes a dynamic.
For example, you could unmarshal your JSON into smth like ResponseObj below:

package main
import (
&quot;encoding/json&quot;
&quot;fmt&quot;
)
type ResponseObj struct {
Response []Item `json:&quot;response&quot;`
}
type Item struct {
TInt   int         `json:&quot;t_int&quot;`
TBool  bool        `json:&quot;t_bool&quot;`
TMixed interface{} `json:&quot;t_null_or_string&quot;`
}
func main() {
json_byte := []byte(`{&quot;response&quot;:[{&quot;t_int&quot;:1, &quot;t_bool&quot;: true,  &quot;t_null_or_string&quot;: null}, {&quot;t_int&quot;:2, &quot;t_bool&quot;: false, &quot;t_null_or_string&quot;: &quot;string1&quot;}]}`)
data_json := ResponseObj{}
if err := json.Unmarshal(json_byte, &amp;data_json); err != nil {
panic(err)
}
fmt.Printf(&quot;%+v\n&quot;, data_json)
}

Your data will look like:

{
Response:
[
{
TInt:1
TBool:true
TMixed:&lt;nil&gt;
}
{
TInt:2
TBool:false
TMixed:string1
}
]
}

And yes, for an attribute with a mixed type you'll run a type assertion (or comparison with nil as in your case or both).

Unlikely your JSON is a total chaos of unpredictable types. Most likely, you can single out a core structure and use interface{} for remaining, mixed types.

Hope this helps.

huangapple
  • 本文由 发表于 2016年12月6日 13:10:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/40988252.html
匿名

发表评论

匿名网友

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

确定