在Go语言中解析具有不同类型的列表的JSON数据。

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

Unmarshal JSON in go with different types in a list

问题

我有困难解析一个JSON结构:

{
  "id": 10,
  "result": [
    {
      "bundled": true,
      "type": "RM-J1100"
    },
    [
      {
        "name": "PowerOff",
        "value": "AAAAAQAAAAEAAAAvAw=="
      },
      {
        "name": "Input",
        "value": "AAAAAQAAAAEAAAAlAw=="
      }
    ]
  ]
}

实际上,我需要从结果中获取第二个切片项。

我目前的尝试是:

type Codes struct {
    Id     int32      `json:"id"`
    Result []interface{} `json:"result"`
}

type ResultList struct {
    Info  InfoMap
    Codes []Code
}

type InfoMap struct {
    Bundled bool   `json:"bundled"`
    Type    string `json:"type"`
}

type Code struct {
    Name  string `json:"name"`
    Value string `json:"value"`
}

输出结果如下:

{10 {{false } []}}

但我也尝试使用以下代码:

type Codes struct {
    Id     int32      `json:"id"`
    Result []interface{} `json:"result"`
}

输出结果是正确的:

{10 [map[type:RM-J1100 bundled:true] [map[name:PowerOff value:AAAAAQAAAAEAAAAvAw==] map[name:Input value:AAAAAQAAAAEAAAAlAw==]]]}

我还可以引用Result1索引:

[map[name:PowerOff value:AAAAAQAAAAEAAAAvAw==] map[name:Input value:AAAAAQAAAAEAAAAlAw==]]

但我无法将接口类型转换为与之匹配的其他类型。有人可以告诉我如何进行接口转换吗?哪种方法是“最佳”的?

英文:

I have trouble unmarschaling a JSON contruct:

{
  "id": 10,
  "result": [
    {
      "bundled": true,
      "type": "RM-J1100"
    },
    [
      {
        "name": "PowerOff",
        "value": "AAAAAQAAAAEAAAAvAw=="
      },
      {
        "name": "Input",
        "value": "AAAAAQAAAAEAAAAlAw=="
      }
    ]
  ]
}

I actually need the second slice item from the result.

My current attempt is

type Codes struct {
	Id     int32      `json:"id"`
	Result []interface{} `json:"result"`
}

type ResultList struct {
	Info  InfoMap
	Codes []Code
}

type InfoMap struct {
	Bundled bool   `json:"bundled"`
	Type    string `json:"type"`
}

type Code struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

the output is like:

{10 {{false } []}}

but I also tried to use this:

type Codes struct {
	Id     int32      `json:"id"`
	Result []interface{} `json:"result"`
}

the output is okay:

{10 [map[type:RM-J1100 bundled:true] [map[name:PowerOff value:AAAAAQAAAAEAAAAvAw==] map[name:Input value:AAAAAQAAAAEAAAAlAw==]]]}

I can also reference the Result1 index:

[map[name:PowerOff value:AAAAAQAAAAEAAAAvAw==] map[name:Input value:AAAAAQAAAAEAAAAlAw==]]

But I fail to convert the interface type to any other Type which would match. Can anybody tell me how to do interface conversion. And what approach would be the "best".

答案1

得分: 13

一种选择是将顶层的内容解析为一个 json.RawMessage 切片。

然后循环遍历成员,并查看每个成员的第一个字符。如果是对象,则将其解析为你的 InfoMap 头部结构体;如果是数组,则将其解析为 Code 结构体的切片。

或者,如果可预测性足够强,可以将第一个成员解析为一个结构体,将第二个成员解析为一个切片。

我在 playground 示例 中演示了这种方法。

type Response struct {
    ID        int               `json:"id"`
    RawResult []json.RawMessage `json:"result"`
    Header    *Header           `json:"-"`
    Values    []*Value          `json:"-"`
}

type Header struct {
    Bundled bool   `json:"bundled"`
    Type    string `json:"type"`
}

type Value struct {
    Name  string `json:"name"`
    Value string `json:"value"`
}

func main() {
    // 省略错误检查
    resp := &Response{}
    json.Unmarshal(rawJ, resp)
    resp.Header = &Header{}
    json.Unmarshal(resp.RawResult[0], resp.Header)
    resp.Values = []*Value{}
    json.Unmarshal(resp.RawResult[1], &resp.Values)
}
英文:

One option would be to unmarshal the top level thing into a slice of json.RawMessage initially.

Then loop through the members, and look at the first character of each one. If it is an object, unmarshal that into your InfoMap header struct, and if it is an array, unmarshal it into a slice of the Code struct.

Or if it is predictable enough, just unmarshal the first member to the one struct and the second to a slice.

I made a playground example of this approach.

type Response struct {
	ID        int               `json:"id"`
	RawResult []json.RawMessage `json:"result"`
	Header    *Header           `json:"-"`
	Values    []*Value          `json:"-"`
}

type Header struct {
	Bundled bool   `json:"bundled"`
	Type    string `json:"type"`
}

type Value struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

func main() {
    //error checks ommitted
	resp := &Response{}
	json.Unmarshal(rawJ, resp)
	resp.Header = &Header{}
	json.Unmarshal(resp.RawResult[0], resp.Header)
	resp.Values = []*Value{}
	json.Unmarshal(resp.RawResult[1], &resp.Values)
}

答案2

得分: -2

你可以使用JSON Marshal / Unmarshal的循环来转换你的结构体。以下是代码示例:

package main

import (
	"encoding/json"
	"log"
)

const (
	inputJSON = `{
	  "id": 10,
	  "result": [
		{
		  "bundled": true,
		  "type": "RM-J1100"
		},
		[
		  {
			"name": "PowerOff",
			"value": "AAAAAQAAAAEAAAAvAw=="
		  },
		  {
			"name": "Input",
			"value": "AAAAAQAAAAEAAAAlAw=="
		  }
		]
	  ]
	}`
)

type Codes struct {
	Id     int32          `json:"id"`
	Result [2]interface{} `json:"result"`
}

type Result struct {
	Info  InfoMap
	Codes []Code
}

type InfoMap struct {
	Bundled bool   `json:"bundled"`
	Type    string `json:"type"`
}

type Code struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

func main() {
	newCodes := &Codes{}
	err := json.Unmarshal([]byte(inputJSON), newCodes)
	if err != nil {
		log.Fatal(err)
	}

	// 打印整个对象
	log.Println(newCodes)

	// 打印 Result 数组
	log.Println(newCodes.Result)

	if len(newCodes.Result) != 2 {
		log.Fatal("Invalid Result struct")
	}

	// Marshal 和 Unmarshal 数据以获取代码列表
	byteCodeList, _ := json.Marshal(newCodes.Result[1])

	codeList := make([]Code, 0)

	err = json.Unmarshal(byteCodeList, &codeList)
	if err != nil {
		log.Fatal("Invalid Code list")
	}

	// 打印 codeList
	log.Println(codeList)
}

你可以在playground上测试它。

英文:

(I will not point out how horrific it is this JSON struct, but as always: sXXt happens)

You can convert your struct like this, by using a cycle of JSON Marshal / Unmarshal. Code follows:

package main
import (
"encoding/json"
"log"
)
const (
inputJSON = `{
"id": 10,
"result": [
{
"bundled": true,
"type": "RM-J1100"
},
[
{
"name": "PowerOff",
"value": "AAAAAQAAAAEAAAAvAw=="
},
{
"name": "Input",
"value": "AAAAAQAAAAEAAAAlAw=="
}
]
]
}`
)
type Codes struct {
Id     int32          `json:"id"`
Result [2]interface{} `json:"result"`
}
type Result struct {
Info  InfoMap
Codes []Code
}
type InfoMap struct {
Bundled bool   `json:"bundled"`
Type    string `json:"type"`
}
type Code struct {
Name  string `json:"name"`
Value string `json:"value"`
}
func main() {
newCodes := &Codes{}
err := json.Unmarshal([]byte(inputJSON), newCodes)
if err != nil {
log.Fatal(err)
}
// Prints the whole object
log.Println(newCodes)
// Prints the Result array (!)
log.Println(newCodes.Result)
if len(newCodes.Result) != 2 {
log.Fatal("Invalid Result struct")
}
// Marshal and Unmarshal data to obtain the code list
byteCodeList, _ := json.Marshal(newCodes.Result[1])
codeList := make([]Code, 0)
err = json.Unmarshal(byteCodeList, &codeList)
if err != nil {
log.Fatal("Invalid Code list")
}
// Prints the codeList
log.Println(codeList)
}

Test it on playground.

huangapple
  • 本文由 发表于 2017年5月5日 12:26:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/43796509.html
匿名

发表评论

匿名网友

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

确定