英文:
Convert yaml to json without struct
问题
以下是翻译好的内容:
服务:
- 订单:
- ID:$save ID1
供应商订单代码:$SupplierOrderCode - ID:$save ID2
供应商订单代码:111111
- ID:$save ID1
我想将这个 YAML 字符串转换为 JSON,因为源数据是动态的,所以无法将其映射到一个结构体中:
var body interface{}
err := yaml.Unmarshal([]byte(s), &body)
然后我想再将该接口转换为 JSON 字符串:
b, _ := json.Marshal(body)
但是出现了一个错误:
panic: json: unsupported type: map[interface {}]interface {}
英文:
Services:
- Orders:
- ID: $save ID1
SupplierOrderCode: $SupplierOrderCode
- ID: $save ID2
SupplierOrderCode: 111111
I want to convert this yaml string to json, cause the source data is dynamic, so I can't map it to a struct:
var body interface{}
err := yaml.Unmarshal([]byte(s), &body)
Then I want to convert that interface to json string again:
b, _ := json.Marshal(body)
But an error occur:
panic: json: unsupported type: map[interface {}]interface {}
答案1
得分: 38
**前言:**我对下面的解决方案进行了优化和改进,并将其发布为一个库,可以在这里找到:github.com/icza/dyno
。下面的convert()
函数可以通过dyno.ConvertMapI2MapS()
来使用。
问题在于,如果你使用最通用的interface{}
类型进行解组,github.com/go-yaml/yaml
包默认使用的类型来解组键值对将是map[interface{}]interface{}
。
第一个想法是使用map[string]interface{}
:
var body map[string]interface{}
但是,如果yaml配置的深度超过一层,这种尝试就会失败,因为body
映射将包含其他类型为map[interface{}]interface{}
的附加映射。
问题在于深度是未知的,而且可能存在除映射之外的其他值,因此使用map[string]map[string]interface{}
不是一个好的选择。
一个可行的方法是让yaml
解组为interface{}
类型的值,并通过递归遍历结果,将遇到的每个map[interface{}]interface{}
转换为map[string]interface{}
值。需要处理映射和切片。
下面是这个转换函数的示例:
func convert(i interface{}) interface{} {
switch x := i.(type) {
case map[interface{}]interface{}:
m2 := map[string]interface{}{}
for k, v := range x {
m2[k.(string)] = convert(v)
}
return m2
case []interface{}:
for i, v := range x {
x[i] = convert(v)
}
}
return i
}
使用它:
func main() {
fmt.Printf("Input: %s\n", s)
var body interface{}
if err := yaml.Unmarshal([]byte(s), &body); err != nil {
panic(err)
}
body = convert(body)
if b, err := json.Marshal(body); err != nil {
panic(err)
} else {
fmt.Printf("Output: %s\n", b)
}
}
const s = `Services:
- Orders:
- ID: $save ID1
SupplierOrderCode: $SupplierOrderCode
- ID: $save ID2
SupplierOrderCode: 111111
`
输出:
Input: Services:
- Orders:
- ID: $save ID1
SupplierOrderCode: $SupplierOrderCode
- ID: $save ID2
SupplierOrderCode: 111111
Output: {"Services":[{"Orders":[
{"ID":"$save ID1","SupplierOrderCode":"$SupplierOrderCode"},
{"ID":"$save ID2","SupplierOrderCode":111111}]}]}
需要注意的一点是,通过将yaml转换为Go映射的JSON,你将失去项目的顺序,因为Go映射中的元素(键值对)是无序的。这可能是一个问题,也可能不是一个问题。
英文:
Foreword: I optimized and improved the below solution, and released it as a library here: github.com/icza/dyno
. The below convert()
function is available as dyno.ConvertMapI2MapS()
.
The problem is that if you use the most generic interface{}
type to unmarshal into, the default type used by the github.com/go-yaml/yaml
package to unmarshal key-value pairs will be map[interface{}]interface{}
.
First idea would be to use map[string]interface{}
:
var body map[string]interface{}
But this attempt falls short if the depth of the yaml config is more than one, as this body
map will contain additional maps whose type will again be map[interface{}]interface{}
.
The problem is that the depth is unknown, and there may be other values than maps, so using map[string]map[string]interface{}
is not good.
A viable approach is to let yaml
unmarshal into a value of type interface{}
, and go through the result recursively, and convert each encountered map[interface{}]interface{}
to a map[string]interface{}
value. Both maps and slices have to be handled.
Here's an example of this converter function:
func convert(i interface{}) interface{} {
switch x := i.(type) {
case map[interface{}]interface{}:
m2 := map[string]interface{}{}
for k, v := range x {
m2[k.(string)] = convert(v)
}
return m2
case []interface{}:
for i, v := range x {
x[i] = convert(v)
}
}
return i
}
And using it:
func main() {
fmt.Printf("Input: %s\n", s)
var body interface{}
if err := yaml.Unmarshal([]byte(s), &body); err != nil {
panic(err)
}
body = convert(body)
if b, err := json.Marshal(body); err != nil {
panic(err)
} else {
fmt.Printf("Output: %s\n", b)
}
}
const s = `Services:
- Orders:
- ID: $save ID1
SupplierOrderCode: $SupplierOrderCode
- ID: $save ID2
SupplierOrderCode: 111111
`
Output:
Input: Services:
- Orders:
- ID: $save ID1
SupplierOrderCode: $SupplierOrderCode
- ID: $save ID2
SupplierOrderCode: 111111
Output: {"Services":[{"Orders":[
{"ID":"$save ID1","SupplierOrderCode":"$SupplierOrderCode"},
{"ID":"$save ID2","SupplierOrderCode":111111}]}]}
One thing to note: by switching from yaml to JSON via Go maps you'll lose the order of the items, as elements (key-value pairs) in a Go map are not ordered. This may or may not be a problem.
答案2
得分: 18
http://sigs.k8s.io/yaml 是一个“围绕 go-yaml 的包装器,旨在在将 YAML 编组到结构体和从结构体解组时提供更好的处理方式”。除其他功能外,它提供了 yaml.YAMLToJSON
方法,应该可以满足你的需求。
英文:
http://sigs.k8s.io/yaml is "a wrapper around go-yaml designed to enable a better way of handling YAML when marshaling to and from structs". Among other things, it provides yaml.YAMLToJSON
method that should do what you want.
答案3
得分: 1
我遇到了同样的问题,icza的答案帮助了我。
此外,我稍微改进了convert()
函数,使其能够访问现有的map[string]interface{}
节点,以深入搜索继承的map[interface{}]interface{}
节点。
func convert(i interface{}) interface{} {
switch x := i.(type) {
case map[interface{}]interface{}:
m2 := map[string]interface{}{}
for k, v := range x {
m2[k.(string)] = convert(v)
}
return m2
case map[string]interface{}:
m2 := map[string]interface{}{}
for k, v := range x {
m2[k] = convert(v)
}
return m2
case []interface{}:
for i, v := range x {
x[i] = convert(v)
}
}
return i
}
英文:
I was having the same issue and the icza's answer helped me.
Additionally I improved the convert()
function a little bit, so it visits existing map[string]interface{}
nodes to search for inherited map[interface{}]interface{}
nodes deep inside.
func convert(i interface{}) interface{} {
switch x := i.(type) {
case map[interface{}]interface{}:
m2 := map[string]interface{}{}
for k, v := range x {
m2[k.(string)] = convert(v)
}
return m2
case map[string]interface{}:
m2 := map[string]interface{}{}
for k, v := range x {
m2[k] = convert(v)
}
return m2
case []interface{}:
for i, v := range x {
x[i] = convert(v)
}
}
return i
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论