Consider JSON, which has a rule that the shape of other keys changes according to the value of a particular key in JSON.For example:
[
{"RGB":{"R":98, "G":218, "B":255}},
{"YCbCr":{"Y":255, "Cb":0, "Cr":-10}}
]
[
{"Space": "RGB", "R":98, "G":218, "B":255},
{"Space": "YCbCr", "Y": 255, "Cb": 0, "Cr":-10}
]
[
{"Space": "RGB", "Point": {"R":98, "G":218, "B":255}},
{"Space": "YCbCr", "Point": {"Y":255, "Cb":0, "Cr":-10}}
]
*The term "○○Tagged" was borrowed from Rust's serde document.
Thus, the main question is to unmarshal JSON with Go, which depends on a specific value.
One such mechanism for unmarshal of JSON is that encoding/json has the type json.RawMessage
.This is supposed to be used to delay unmarshal by keeping the raw string before unmarshal, and unmarshal if necessary when using it.
If Adjacently tagged, the specific source code is Tag and subsequent RawMessage. In the case of Internally tagged, for example, you can write the flow of unmarshal only once, switch to that value, and unmarshal the whole thing again.The sample code looks like this.It's a little awkward to unmarshal the whole thing twice. The hard part of using Existing stack overflow questions also found unmarshal as a map. However, if you accept it as a map, you will not benefit from the system: For the same reason, unmarshal as Map and Is there a way to unmarshal JSON that dynamically changes shape depending on the tag? It is better not to unmarshal every time you use it, but to unmarshal once you use it, you don't have to unmarshal after that.Also, I would like to benefit from the type as much as possible, and I would appreciate it if you could find out the error at the first unmarshal. Environment: Go 1.14 json.RawMessage
is that whenever you use it as a RawMessage, you need to unmarshal it.If possible, I would like to write it so that I can use the data around without unmarshal once.
map[string]interface{}
does not cause errors if int is written in the key where string is expected. Unmarshal as map[string]string
does not support keys other than string, such as int and structure.interface{}
and type assertions when used are a bit tricky.interface{}
are convenient when you want to unmarshal in a loose and fluffy way, but I would like to take a closer look at them this time.In this direction, libraries dproxy and jsonpointer are known to be useful.
I found a way to use the Sample Code for Internally tagged: Now unmarshal is once. The disadvantages are that we compare tags with string, and that we must make a successful assertion (which is practically tagged union).I would appreciate it if you could let me know if there is any way I can do anything about this area in the comments or in a separate answer.Do you think you'll do well with go generate...?UnmarshalJSON
method.The type with this method is treated as json.Unmarshaler
and can be used by json.Unmarshal
.
type Color structure {
Space string
Content interface {}
}
type RGB structure {
Ruint 8
Guint 8
Buint 8
}
US>type YCbCrruct{
Youint8
Cbint8
Crint 8
}
func(c*Color)UnmarshalJSON(data[]byte)error{
// By convention, Unmarshals implement UnmarshalJSON([]byte("null") asano-op.
// TODO: ↑ Is this the correct interpretation of this sentence?
if bytes.Equal(data, []byte("null")){
return nil
}
varspace=struct{
Space string
}{}
err —=json.Unmarshal (data, & space)
if err!=nil{
return fmt.Errorf("Space not found: %w", error)
}
c.Space=space.Space
switchspace.Space{
case "RGB":
varrgb RGB
iferr:=json.Unmarshal(data, & rgb);err!=nil{
return fmt.Errorf("Space says this is RGB, but cannot unmarshal as RGB: %w", err)
}
c. Content=rgb
case "YCbCr":
varycbcr YCbCr
iferr:=json.Unmarshal(data, & ycbcr);err!=nil{
return fmt.Errorf("Space says this is YCbCr, but cannot unmarshal as YCbCr: %w", error)
}
c. Content=ycbcr
default:
return errors.New("Unknown Space:"+space.Space")
}
return nil
}
funcmain(){
varj=[]byte(`[
{"Space": "YCbCr", "Y": 255, "Cb": 0, "Cr":-10},
{"Space": "RGB", "R":98, "G":218, "B":255}
]`)
var colors [ ]Color
err: = json.Unmarshal(j, & colors)
if err!=nil{
log.Fatal(err)
}
for_,c:=range colors{
switch c.Space {
case "RGB":
rgb,_ —=c.Content. (RGB)
fmt.Println(c.Space,rgb)
case "YCbCr":
ycbcr,_ —=c.Content.(YCbCr)
fmt.Println(c.Space, ycbcr)
}
}
}
© 2024 OneMinuteCode. All rights reserved.