如何解组具有不同类型数组的 JSON 数据?

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

How to unmarshal JSOn with an array of different types

问题

我有一个需要解析为golang类型的JSON,内容如下:

{
    "name": "something",
    "rules": [
        {
            "itemTypeBasedConditions": [["containsAny", ["first_match", "second_match"]]],
            "validity": "INVALID"
        }
    ]
}

问题是,在itemTypeBasedConditions中,每个数组的数组都包含了一组字符串(始终是第一个元素)和另一个数组(第二个元素),我不确定如何将所有这些解析为一个对象,然后对其进行操作。

我已经定义了以下结构体:

type RulesFile struct {
    Name  string
    Rules []RulesItem
}

type RulesItem struct {
    ItemTypeBasedConditions [][]interface{}
    Validity                string
}

然后,我猜我需要逐个将接口类型的元素转换为字符串(containsAny)或字符串数组("first_match","second_match")。

有没有更好的方法来解析这个JSON?

英文:

I have JSON like this that I need to parse into a golang type:

{
    name: "something"
    rules: [
    {
      "itemTypeBasedConditions": [["containsAny", ["first_match", "second_match"]]],
      "validity": "INVALID"
    }]
}

The problem is that each array of the array in itemTypeBasedConditions contains a mix of strings (always first element) and another array (second element), and I am not sure how to parse all of that into an object that I could then manipulate.

I got to:

type RulesFile struct {
    Name string
    Rules []RulesItem
}

type RulesItem struct {
    itemTypeBasedConditions [][]interface{}
    validity bool
}

And then I guess I have to convert elements one by one from interface{} to either string (containsAny) or an array of strings ("first_match", "second_match")

Is there a better way of approaching this JSON parsing?

答案1

得分: 2

我会这样做,你可以根据你的需求进行修改。

package main

import (
	"encoding/json"
	"fmt"
	"os"
	"reflect"
)

type RulesFile struct {
	Name  string      `json:"name"`
	Rules []RulesItem `json:"rules"`
}

type RulesItem struct {
	ItemTypeBasedConditions [][]Condition `json:"itemTypeBasedConditions"`
	Validity                bool          `json:"validity"`
}

type Condition struct {
	Value *string
	Array *[]string
}

func (c Condition) String() string {
	if c.Value != nil {
		return *c.Value
	}

	return fmt.Sprintf("%v", *c.Array)
}

func (c *Condition) UnmarshalJSON(data []byte) error {

	var y interface{}

	err := json.Unmarshal(data, &y)
	if err != nil {
		return err
	}
	switch reflect.TypeOf(y).String() {
	case "string":
		val := fmt.Sprintf("%v", y)
		c.Value = &val
		return nil
	case "[]interface {}":
		temp := y.([]interface{})
		a := make([]string, len(temp))
		for i, v := range temp {
			a[i] = fmt.Sprint(v)
		}
		c.Array = &a
		return nil
	}

	return fmt.Errorf("cannot unmarshall into string or []string: %v", y)
}

var input string = `
{
	"name": "something",
    "rules": [
		{
			"itemTypeBasedConditions": [["containsAny",["first_match", "second_match"]]],
			"validity": false
		}
	]
}`

func main() {
	var ruleFile RulesFile
	err := json.Unmarshal([]byte(input), &ruleFile)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	fmt.Printf("%+v\n", ruleFile)
}

希望对你有帮助!

英文:

I would do something like this, you can probably alter this to your needs.

package main

import (
	"encoding/json"
	"fmt"
	"os"
	"reflect"
)

type RulesFile struct {
	Name  string      `json:"name"`
	Rules []RulesItem `json:"rules"`
}

type RulesItem struct {
	ItemTypeBasedConditions [][]Condition `json:"itemTypeBasedConditions"`
	Validity                bool          `json:"validity"`
}

type Condition struct {
	Value *string
	Array *[]string
}

func (c Condition) String() string {
	if c.Value != nil {
		return *c.Value
	}

	return fmt.Sprintf("%v", *c.Array)
}

func (c *Condition) UnmarshalJSON(data []byte) error {

	var y interface{}

	err := json.Unmarshal(data, &y)
	if err != nil {
		return err
	}
	switch reflect.TypeOf(y).String() {
	case "string":
		val := fmt.Sprintf("%v", y)
		c.Value = &val
		return nil
	case "[]interface {}":
		temp := y.([]interface{})
		a := make([]string, len(temp))
		for i, v := range temp {
			a[i] = fmt.Sprint(v)
		}
		c.Array = &a
		return nil
	}

	return fmt.Errorf("cannot unmarshall into string or []string: %v", y)
}

var input string = `
{
	"name": "something",
    "rules": [
		{
			"itemTypeBasedConditions": [["containsAny",["first_match", "second_match"]]],
			"validity": false
		}
	]
}`

func main() {
	var ruleFile RulesFile
	err := json.Unmarshal([]byte(input), &ruleFile)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	fmt.Printf("%+v\n", ruleFile)
}

答案2

得分: 0

你可以实现json.Unmarshaler接口。首先,将json解组成一个json.RawMessage切片,然后,一旦完成,你可以将各个元素解组为相应的类型。

type Cond struct {
    Name string
    Args []string
}

func (c *Cond) UnmarshalJSON(data []byte) error {
    // 解组为一个原始json切片
    var raw []json.RawMessage
    if err := json.Unmarshal(data, &raw); err != nil {
        return err
    } else if len(raw) != 2 {
        return errors.New("不支持的条件元素数量")
    }

    // 将第一个原始json元素解组为字符串
    if err := json.Unmarshal(raw[0], &c.Name); err != nil {
        return err
    }

    // 将第二个原始json元素解组为字符串切片
    return json.Unmarshal(raw[1], &c.Args)
}

https://go.dev/play/p/-tbr73TvX0d

英文:

You can implement the json.Unmarshaler interface. Have that implementation first unmarshal the json into a slice of json.RawMessage, then, once you've done that, you can unmarshal the individual elements to their corresponding types.

type Cond struct {
	Name string
	Args []string
}

func (c *Cond) UnmarshalJSON(data []byte) error {
    // unmarshal into a slice of raw json
	var raw []json.RawMessage
	if err := json.Unmarshal(data, &raw); err != nil {
		return err
	} else if len(raw) != 2 {
		return errors.New("unsupported number of elements in condition")
	}

    // unmarshal the first raw json element into a string
	if err := json.Unmarshal(raw[0], &c.Name); err != nil {
		return err
	}

    // unmarshal the second raw json element into a slice of string
	return json.Unmarshal(raw[1], &c.Args)
}

https://go.dev/play/p/-tbr73TvX0d

huangapple
  • 本文由 发表于 2022年4月7日 06:59:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/71774502.html
匿名

发表评论

匿名网友

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

确定