可以修改json.RawMessage吗?

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

Can I modify json.RawMessage?

问题

这是对https://stackoverflow.com/questions/33622016/json-sometimes-array-sometimes-object/的后续问题。

在原始问题中,我问了如何处理以下情况:“我正在使用一个可能返回变量字符串或返回变量数组的json API”。

我有一个解决方案,但我想知道是否有办法修改json.RawMessage?

而不是通过查看RawMessage中的 [ 或 { 字符来确定对象是数组还是字符串,如果我总是将RawMessage变量字符串转换为数组会怎样?

这样,我就不必为字符串和数组编写所有的访问器。我只需要处理数组即可。

所以我的问题是:有办法修改json.RawMessage吗?

例如:

将这个:

{ 
  "net": {
	"comment": {
        "line":
            {
                "$": "All abuse issues will only be responded to by the Abuse",
                "@number": "0"
            }
    }
}

转换为这个:

{ 
  "net": {
	"comment": {
        "line": [
            {
                "$": "All abuse issues will only be responded to by the Abuse",
                "@number": "0"
            }
        ]
    }
}

这样,当我解组到我的结构体中时,只有一种类型的comment.line,只有line[]而不是line[]和line。

提前感谢。

我是一个刚开始接触Go语言的新手,我正在努力理解将数据解组到强类型语言中的困难。

英文:

This is a follow up to https://stackoverflow.com/questions/33622016/json-sometimes-array-sometimes-object/

In the original question, I asked how to deal with: "I am consuming a json API that might return a string for a variable or might return an array for a variable"

I have a solution but I was wondering, is there a way to modify json.RawMessage?

Rather then if/then looking at the RawMessage for a [ or { char to determine if the object is an array or an string, what if I always took a RawMessage variable string and turned it into an array?

This way, I don't have to code all of the accessors for BOTH strings AND arrays. I could simply deal with arrays.

So my question is: Is there a way to modify the json.RawMessage?

eg:

Turn this:

{ 
  "net": {
	"comment": {
        "line":
            {
                "$": "All abuse issues will only be responded to by the Abuse",
                "@number": "0"
            }
    }
}

Into This:

{ 
  "net": {
	"comment": {
        "line": [
            {
                "$": "All abuse issues will only be responded to by the Abuse",
                "@number": "0"
            }
        ]
    }
}

So, that way, when I unmarshal into my struct, there is only 1 type of comment.line, Just line[] vs line[] AND line.

Thanks in advance.

I am a golang neophyte and I'm just getting my head wrapped around the difficulties of unmarshaling into an strongly typed language.

答案1

得分: 2

是的,你可以编辑json.RawMessage类型,因为它只是[]byte的别名。

也就是说,你不需要保留原始类型,只需创建自己的数组类型,并在自定义的Unmarshal函数中将标量值转换为数组。

以下是一个示例(在Play上)。

我们在这里所做的就是检查MagicArray的字节是否以'['开头,如果是,则按照正常方式进行解组。否则,解组并追加到切片中。

你将需要为想要按此方式工作的每种类型实现自定义数组,但这仍然可能比尝试安全地操作JSON二进制数据以将标量值强制转换为数组要好。

这种方法的另一个附加好处是你可以在流式JSON解码器中使用它,例如json.NewDecoder(reader).Decode(&obj)

package main

import "encoding/json"
import "log"

type MagicArray []interface{}

func (ma *MagicArray) UnmarshalJSON(b []byte) error {
    if b[0] == '[' {
        return json.Unmarshal(b, (*[]interface{})(ma))
    } else {
        var obj interface{}
        if err := json.Unmarshal(b, &obj); err != nil {
            return err
        }
        *ma = append(*ma, obj)
    }
    return nil
}

func main() {
    myStruct := struct {
        A MagicArray
        B MagicArray
    }{}

    err := json.Unmarshal(jsonToDecode, &myStruct)
    if err != nil {
        log.Println("Fail:", err)
    } else {
        log.Println(myStruct)
    }
}

var jsonToDecode = []byte(`
    { 
    "A": "I am not an array", 
    "B":["I am an array"]
    }
`)
英文:

Yes, you can edit json.RawMessage type as it is simply an alias for []byte.

That said, you don't need to keep raw type, just make your own implementation of the array type and in your custom Unmarshal function, make scalars an array.

Here's an example (on Play).

All we do here is see if the bytes for MagicArray starts with '[', if so, just unmarshal as normal. Otherwise, Unmarshal and append to slice.

you will have to implement custom array for each type you want to work like this, but that's still probably better than trying to safely manipulate the json binary to try coerce the scalars into arrays.

Another side benefit to this approach is you can it with the streaming json decoder, as in json.NewDecoder(reader).Decode(&obj)

package main

import "encoding/json"
import "log"

type MagicArray []interface{}

func (ma *MagicArray) UnmarshalJSON(b []byte) error {
	if b[0] == '[' {
		return json.Unmarshal(b, (*[]interface{})(ma))
	} else {
		var obj interface{}
		if err := json.Unmarshal(b, &obj); err != nil {
			return err
		}
		*ma = append(*ma, obj)
	}
	return nil
}
func main() {
	myStruct := struct {
		A MagicArray
		B MagicArray
	}{}

	err := json.Unmarshal(jsonToDecode, &myStruct)
	if err != nil {
		log.Println("Fail:", err)
	} else {
		log.Println(myStruct)
	}
}

var jsonToDecode = []byte(`
	{ 
	"A": "I am not an array", 
	"B":["I am an array"]
	}
`)

答案2

得分: 2

我认为David有一个很好(更好)的答案,但是直接回答你的问题:是的,如果你小心的话,你可以修改json.RawMessage。它的声明是type json.RawMessage []byte,意味着它在底层只是[]byte的另一个名称。你可以将它转换为[]bytestring,进行修改,然后再转换回来。

在不经过深思熟虑的情况下对序列化数据进行字符串操作是不应该做的事情,但是在将[]包装在一个JSON对象周围的情况下,很容易证明它是安全的。如果msg是表示对象的json.RawMessage,那么

json.RawMessage("[" + string(msg) + "]")

是我认为可读性较好的方法,用于创建表示包含该对象的数组的RawMessage 可以修改json.RawMessage吗?

英文:

I think that David has a good (better) answer, but to answer your question directly: yes, you can modify a json.RawMessage if you're careful. it's declared as type json.RawMessage []byte, meaning it's just another name for []byte under the hood. You can cast it to []byte or string, modify it, and cast it back.

Doing string options on serialized data isn't the kind of thing you should do without thinking about the consequences, but in the case of wrapping [ and ] around a JSON object, it's not too hard to prove that it should be safe. If msg is a json.RawMessage representing an object, then

json.RawMessage("[" + string(msg) + "]")

is what I would consider a readable approach to making a RawMessage representing an array containing that object 可以修改json.RawMessage吗?

huangapple
  • 本文由 发表于 2015年11月12日 11:22:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/33663884.html
匿名

发表评论

匿名网友

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

确定