如何解组不同类型的流

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

How to Unmarshal stream of different types

问题

我有一串不同但已知类型的对象流,我正在努力弄清楚如何将JSON编码的流解组为Go结构体。

这是一个示例切片,仅用于说明目的:

[
	{
		"uid": "xyz1",
		"type": "person",
		"name": "John",
		"surname": "King"
	},
	{
		"uid": "xyz2",
		"type": "thing",
		"car": "benz",
		"shoes": "nike"
	},
	{
		"uid": "xyz3",
		"type": "person",
		"name": "Mary",
		"surname": "Queen"
	}
]

如您所见,有一个type字段,用于通知接收方对象的类型。

数据在流中映射到以下Go类型,它们“共享”UIDType字段:

type Person struct {
	UID     string
	Type    string
	Name    string
	Surname string
}

type Thing struct {
	UID   string
	Type  string
	Car   string
	Shoes string
}

我在这里找到了一个类似的问题链接,尽管没有答案,但与我需要处理的情况有些不同。与其保证有一个transaction键空间,我无法保证负载除了typeuid字段之外的样子。

除了深入使用reflect包之外,是否有其他方法可以实现这一点呢?

英文:

I have a stream of objects of different but known, types and I'm struggling to figure out how to unmarshal the JSON encoded stream into Go structs.

Here is a sample slice for illustration purpose

[
	{
		"uid": "xyz1",
		"type": "person",
		"name": "John",
		"surname": "King"
	},
	{
		"uid": "xyz2",
		"type": "thing",
		"car": "benz",
		"shoes": "nike"
	},
	{
		"uid": "xyz3",
		"type": "person",
		"name": "Mary",
		"surname": "Queen"
	}
]

As you can see there is a type field that informs the receiver about what is the type of the object.

Data maps into the following Go types in the stream which "share" UID and Type fields:

type Person struct {
	UID     string
	Type    string
	Name    string
	Surname string
}

type Thing struct {
	UID   string
	Type  string
	Car   string
	Shoes string
}

I found a similar question here, though unanswered, it's a bit different from what I need to deal with. Instead of being guaranteed a transaction keyspace as I've no guarantee what the payload should look like besides the type and uid fields.

Is there some way to this other than drowning deeply in reflect package?

答案1

得分: 1

将其翻译为中文如下:

解析为一个 原始消息 的切片:

var elems []json.RawMessage
err := json.Unmarshal(data, &elems)
if err != nil {
    log.Fatal(err)
}

对于切片中的每个元素,先解析一次以找到类型,然后再解析一次以得到具体类型:

for _, elem := range elems {
    // 提取类型字段
    var t struct {
        Type string
    }
    err := json.Unmarshal(elem, &t)
    if err != nil {
        log.Fatal(err)
    }

    // 根据类型字段的值解码为具体类型
    switch t.Type {
    case "person":
        var p Person
        err := json.Unmarshal(elem, &p)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("%#v\n", p)
    case "thing":
        var t Thing
        err := json.Unmarshal(elem, &t)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("%#v\n", t)
    default:
        fmt.Printf("未知类型 %s\n", t.Type)
    }
}

在 Go playground 上运行代码:点击这里

英文:

Unmarshal to a slice of raw messages:

var elems []json.RawMessage
err := json.Unmarshal(data, &elems)
if err != nil {
	log.Fatal(err)
}

For each element in the slice, unmarshal once to find the type and a second time to the specific type:

for _, elem := range elems {

    // Pick out the type field.

	var t struct {
		Type string
	}
	err := json.Unmarshal(elem, &t)
	if err != nil {
		log.Fatal(err)
	}

    // Decode to specific type based on value of the type field.

	switch t.Type {
	case "person":
		var p Person
		err := json.Unmarshal(elem, &p)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%#v\n", p)
	case "thing":
		var t Thing
		err := json.Unmarshal(elem, &t)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%#v\n", t)
	default:
		fmt.Printf("unknown type %s\n", t.Type)
	}

}

Run it on the Go playground.

huangapple
  • 本文由 发表于 2021年2月26日 03:14:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/66374990.html
匿名

发表评论

匿名网友

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

确定