3 ms·
This is the improvement that I’m most excited about for this release. I waded through some code recently that implemented its own validation in a hacks way by t
by jitl 9y ago
This is the improvement that I’m most excited about for this release. I waded through some code recently that implemented its own validation in a hacks way by text-digging the re-serialization of structs... We were unable to put default (zero) values in our config files...
- majewsky 9y ago> We were unable to put default (zero) values in our config files... Huh? I don't know the details of what you're (de)serializing, but the standard strategy for default values that are not zero values is to have unpack into a pointer. For example, if you have a message like { "foo": 23, "bar": 42 } and the "bar" field has a default value of 5, you unpack like: var msg struct { Foo int Bar *int } err := json.Unmarshal(data, &msg) //handle error if msg.Bar == nil { defaultValue := 5 msg.Bar = &defaultValue } If you cannot change the type of the message to have a pointer field, you can implement json.Unmarshaler for the receiving type such that UnmarshalJSON deserializes into a temporary type, then copies the values into the recipient: type Message struct { Foo int Bar int } func (m *Message) UnmarshalJSON(in []byte) error { var msg struct { Foo int Bar *int } err := json.Unmarshal(data, &msg) if err != nil { return err } m.Foo = msg.Foo m.Bar = 5 if msg.Bar != nil { m.Bar = *msg.Bar } return nil } Now you can deserialize into Message (or into a struct containing a Message or *Message or []Message or whatever) and it will fill in the correct default value.
- jerf 9y agoOne thing you may want to consider in the future, or, at least, whoever wrote that code, is that it's not that difficult to fork the standard library and make it do something different in your program. I forked the encoding/xml library once so I could add tag stack depth limits and size limits for tags and text nodes, so I could use it to write an XML validator (in which you must be careful that the user can not simply blow out your RAM by sending you too much text), and I forked encoding/json so that you could declare a map[string]interface{} field on an struct you are decoding into that will "catch" any unmatched keys in the JSON object and treat it the same as if you passed the map[string]interface{} in as the thing to decode into, instead of discarding them. (Encoding works too.) The first was on the order of two days and the second was on the order of four hours (untested in production, but the unit tests are fairly easy and comprehensive), though in both of those particular cases if you've not fiddled with the "reflect" package enough to understand it, you'll also need to pay that price. Other packages may require other knowledge, of course. But the time required is such that it may be less than the time spent on dirty, half-working hacks, which tends to grow from the "just five minutes" of the initial estimate to "five days and we're still paying for the hack five years later...".
- shabbyrobe 9y agoYou can have whatever default value you like by initialising the struct before you pass it to json.Unmarshal. If the key isn't present, it won't be set: type Test struct { Foo string Bar string } func NewTest() *Test { return &Test{Foo: "a"} } func main() { t := NewTest() if err := json.Unmarshal([]byte(`{"Bar": "b"}`), t); err != nil { log.Fatal(err) } fmt.Printf("%#v\n", t) } Output: &main.Test{Foo:"a", Bar:"b"}