tinygo åãã® JSON marshaler: go-json-ice ãæ¸ãã
English article is here: Released go-json-ice: a code generator of JSON marshaler for tinygo - moznion's tech blog
tinygo ã§ã¯ encoding/json
ã import ããã¨ã³ã³ãã¤ã«ã§ããªããªãã¨ããåé¡ããã *1ï¼ãªãããã® struct ã JSON ã« marshal ãããæã«ä½¿ãã de facto ãªæ¹æ³ãç¡ãããã«è¦ãã¾ããï¼ããã«é¢ãã¦ã¯ä¾ãã°ä»¥ä¸ã®ãã㪠issue ãç«ã£ã¦ãã¾ã:
ã¤ã¾ã tinygo ä¸ã§ä»»æã® struct ã JSON ã«ãããæã¯ãæã§æ°ãä»ãã¦ã·ãªã¢ã«åãããããæ¹æ³ããªãã£ãããã§ããï¼ã¾ãããã ã¨ä½ãã¨ä¸ä¾¿ã ã£ãã®ã§è¡¨é¡ã®éã json-ice ã¨ãã encoding/json
ã«ä¾åããªã JSON marshaler ã®ã³ã¼ãã¸ã§ãã¬ã¼ã¿ãä½ãã¾ããï¼
æåã¨ãã¦ã¯ï¼äºåã« marshaling 対象ã¨ãªã struct (ã® json
ã«ã¹ã¿ã struct ã¿ã°) ã解éã㦠JSON ã« marshal ããã³ã¼ããåãåºãï¼ã¨ããè³ã£ã¦ã·ã³ãã«ãªãã®ã¨ãªãã¾ãï¼ä¼¼ããããªæåãããå
è¡å®è£
ã« mailru/easyjson ãªã©ãããã¾ããï¼ãããã¯å
é¨çã« encoding/json
ã«ä¾åãã¦ããããã§ï¼ä»åã®ç¨éã«ã¯ããããã¾ããã§ããï¼
ä¾ãã°ä»¥ä¸ã®ãã㪠struct ã marshal ãããæã«ã¯ go:generate
ã¨ä¸ç·ã«ã³ã¼ããæ¸ãã¦ããã¨
//go:generate json-ice --type=AwesomeStruct type AwesomeStruct struct { Foo string `json:"foo"` Bar string `json:"bar,omitempty"` }
MarshalAwesomeStructAsJSON(s *AwesomeStruct) ([]byte, error)
ã¨ããã³ã¼ããçæãããã®ã§ï¼ãããå©ç¨ã㦠struct ã JSON ã« marshal ãããã¨ãå¯è½ã§ã:
marshaled, err := MarshalAwesomeStructAsJSON(&AwesomeStruct{ Foo: "buz", Bar: "", }) if err != nil { log.Fatal(err) } fmt.Printf("%s\n", marshaled) // => {"foo":"buz"}
ããã«ããå®è¡æã« reflection ã使ã£ã¦åçã« marshaling ããå¿
è¦ããªããªãã®ã§ï¼tinygo ã§ã JSON marshaling ãç°¡åã«è¡ããããã«ãªãã¾ãï¼ã¾ãï¼åç㪠reflection ã®ä»£ããã«äºåè¨ç®ããã®ã§å½ç¶ã®çµæã§ããããã©ã¼ãã³ã¹ãå°ãè¯ããªãã¾ã *2ï¼
ãã¡ãããã®å¯ä½ç¨ã¨ã㦠interface{}
ãªå¤ãæ㤠struct ã«ã¤ãã¦ã¯åçãªåã®è§£æ±ºãã§ããªããã marshaling ãã§ãã¾ããï¼Marshaling ããããã«ã¯éçã«åã®è§£æ±ºãã§ããå¿
è¦ãããã¾ãï¼
ã¾ã tinygo 㯠wasm ãåãåºãæ©è½ãæãã¦ããï¼ãã® wasm ãå®è¡æã« import
ããã¢ã¸ã¥ã¼ã«ã¯ãå
ã®ã³ã¼ããä½ã«ä¾åãã¦ããããã«ãã£ã¦å¤åãã¦ãã¾ãï¼ãã®å®è¡æã®ä¾åãã§ããéããããã ã«ããã (ä¾ãã°ãã©ã¦ã¶ã©ã³ã¿ã¤ã 以å¤ã®å¼·å㪠sandbox ç°å¢ã§ wasm ãåããã¨ããã¦ã¼ã¹ã±ã¼ã¹ãèãããã) ã¨ããåæ©ããã£ãã®ã§ï¼çæã³ã¼ããä¾åããããã±ã¼ã¸ã¯å¯è½ãªéãæå°éã«ã¨ã©ãã¾ããï¼çµæçã«ç¾æç¹ã§ã¯ strconv
ã«ã®ã¿ä¾åããããã«ãªã£ã¦ãã¾ãï¼ãããã«ï¼
ãããªæãã®ã©ã¤ãã©ãªã§ãï¼ã©ãããå©ç¨ãã ããï¼
ãã¡ãã tinygo ã§ã¯ãªãé常㮠go ã®å¦çç³»ã§ãå©ç¨ã§ãã¾ããï¼ããã«ã¤ãã¦ã¯ãã£ã¨è¯ãå
è¡å®è£
(ãããã easyjson ã¨ã) ãããã¨æãã®ã§ï¼ãã¡ãã®å©ç¨ã®æ¤è¨ããããããã¾ãï¼
ãªãï¼ãã®å®è£ 㯠JSON ã® marshaling ã®ã¿ããµãã¼ããããã®ã§ããï¼éã« tinygo 㧠JSON unmarshaling ããã«ã¯ã©ãããã°ãããã¨è¨ãã¨ï¼buger/jsonparser ãå©ç¨ããã°è¯ãããã«æãã¾ããï¼
> It does not rely on encoding/json, reflection or interface{}, the only real package dependency is bytes.
ä½è«ã§ãã
//go:generate json-ice --type=DeepStruct type DeepStruct struct { Deep []map[string]map[string]map[string]map[string]string `json:"deep"` }
ã®ãããªæ·±ãï¼å帰çï¼ï¼ï¼ãªåã«ã¤ãã¦ãã¡ããã¨ããã³ã¼ãçæãå¯è½ã§ã:
given := &DeepStruct{ Deep: []map[string]map[string]map[string]map[string]string{ { "foo": { "bar": { "buz": { "qux": "foobar", }, }, }, }, { "foofoo": { "barbar": { "buzbuz": { "quxqux": "foobarfoobar", }, }, }, }, }, } marshaled, err := MarshalDeepStructAsJSON(given) if err != nil { log.Fatal(err) } log.Printf("[debug] %s", marshaled) // => {"deep":[{"foo":{"bar":{"buz":{"qux":"foobar"}}}},{"foofoo":{"barbar":{"buzbuz":{"quxqux":"foobarfoobar"}}}}]}
çæã³ã¼ãã¯ãããªæã
import "github.com/moznion/go-json-ice/serializer" func MarshalDeepStructAsJSON(s *DeepStruct) ([]byte, error) { buff := make([]byte, 1, 54) buff[0] = '{' if s.Deep == nil { buff = append(buff, "\"deep\":null,"...) } else { buff = append(buff, "\"deep\":"...) buff = append(buff, '[') for _, v := range s.Deep { if v == nil { buff = append(buff, "null"...) } else { buff = append(buff, '{') for mapKey, mapValue := range v { buff = serializer.AppendSerializedString(buff, mapKey) buff = append(buff, ':') if mapValue == nil { buff = append(buff, "null"...) } else { buff = append(buff, '{') for mapKey, mapValue := range mapValue { buff = serializer.AppendSerializedString(buff, mapKey) buff = append(buff, ':') if mapValue == nil { buff = append(buff, "null"...) } else { buff = append(buff, '{') for mapKey, mapValue := range mapValue { buff = serializer.AppendSerializedString(buff, mapKey) buff = append(buff, ':') if mapValue == nil { buff = append(buff, "null"...) } else { buff = append(buff, '{') for mapKey, mapValue := range mapValue { buff = serializer.AppendSerializedString(buff, mapKey) buff = append(buff, ':') buff = serializer.AppendSerializedString(buff, mapValue) buff = append(buff, ',') } if buff[len(buff)-1] == ',' { buff[len(buff)-1] = '}' } else { buff = append(buff, '}') } } buff = append(buff, ',') } if buff[len(buff)-1] == ',' { buff[len(buff)-1] = '}' } else { buff = append(buff, '}') } } buff = append(buff, ',') } if buff[len(buff)-1] == ',' { buff[len(buff)-1] = '}' } else { buff = append(buff, '}') } } buff = append(buff, ',') } if buff[len(buff)-1] == ',' { buff[len(buff)-1] = '}' } else { buff = append(buff, '}') } } buff = append(buff, ',') } if buff[len(buff)-1] == ',' { buff[len(buff)-1] = ']' } else { buff = append(buff, ']') } buff = append(buff, ',') } if buff[len(buff)-1] == ',' { buff[len(buff)-1] = '}' } else { buff = append(buff, '}') } return buff, nil }
*1:reflection å¨ãã®ãµãã¼ããååã§ãªããã: https://tinygo.org/lang-support/stdlib/#encoding-json