mirror of
https://github.com/goccy/go-yaml.git
synced 2025-11-02 22:31:05 +00:00
Supports decoding for null value
This commit is contained in:
parent
93521ac450
commit
4a90c16927
6 changed files with 83 additions and 6 deletions
37
decode.go
37
decode.go
|
|
@ -228,6 +228,11 @@ func (d *Decoder) decodeValue(dst reflect.Value, src ast.Node) error {
|
||||||
if dst.IsNil() {
|
if dst.IsNil() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if src.Type() == ast.NullType {
|
||||||
|
// set nil value to pointer
|
||||||
|
dst.Set(reflect.Zero(valueType))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
v := d.createDecodableValue(dst.Type())
|
v := d.createDecodableValue(dst.Type())
|
||||||
if err := d.decodeValue(v, src); err != nil {
|
if err := d.decodeValue(v, src); err != nil {
|
||||||
return errors.Wrapf(err, "failed to decode ptr value")
|
return errors.Wrapf(err, "failed to decode ptr value")
|
||||||
|
|
@ -399,6 +404,11 @@ func (d *Decoder) decodeStruct(dst reflect.Value, src ast.Node) error {
|
||||||
if !fieldValue.CanSet() {
|
if !fieldValue.CanSet() {
|
||||||
return xerrors.Errorf("cannot set embedded type as unexported field %s.%s", field.PkgPath, field.Name)
|
return xerrors.Errorf("cannot set embedded type as unexported field %s.%s", field.PkgPath, field.Name)
|
||||||
}
|
}
|
||||||
|
if fieldValue.Type().Kind() == reflect.Ptr && src.Type() == ast.NullType {
|
||||||
|
// set nil value to pointer
|
||||||
|
fieldValue.Set(reflect.Zero(fieldValue.Type()))
|
||||||
|
continue
|
||||||
|
}
|
||||||
newFieldValue := d.createDecodableValue(fieldValue.Type())
|
newFieldValue := d.createDecodableValue(fieldValue.Type())
|
||||||
if err := d.decodeValue(newFieldValue, src); err != nil {
|
if err := d.decodeValue(newFieldValue, src); err != nil {
|
||||||
if xerrors.Is(err, errOverflowNumber) {
|
if xerrors.Is(err, errOverflowNumber) {
|
||||||
|
|
@ -416,6 +426,11 @@ func (d *Decoder) decodeStruct(dst reflect.Value, src ast.Node) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fieldValue := structValue.Elem().FieldByName(field.Name)
|
fieldValue := structValue.Elem().FieldByName(field.Name)
|
||||||
|
if fieldValue.Type().Kind() == reflect.Ptr && src.Type() == ast.NullType {
|
||||||
|
// set nil value to pointer
|
||||||
|
fieldValue.Set(reflect.Zero(fieldValue.Type()))
|
||||||
|
continue
|
||||||
|
}
|
||||||
newFieldValue := d.createDecodableValue(fieldValue.Type())
|
newFieldValue := d.createDecodableValue(fieldValue.Type())
|
||||||
if err := d.decodeValue(newFieldValue, v); err != nil {
|
if err := d.decodeValue(newFieldValue, v); err != nil {
|
||||||
if xerrors.Is(err, errOverflowNumber) {
|
if xerrors.Is(err, errOverflowNumber) {
|
||||||
|
|
@ -461,6 +476,11 @@ func (d *Decoder) decodeSlice(dst reflect.Value, src ast.Node) error {
|
||||||
elemType := sliceType.Elem()
|
elemType := sliceType.Elem()
|
||||||
for iter.Next() {
|
for iter.Next() {
|
||||||
v := iter.Value()
|
v := iter.Value()
|
||||||
|
if elemType.Kind() == reflect.Ptr && v.Type() == ast.NullType {
|
||||||
|
// set nil value to pointer
|
||||||
|
sliceValue = reflect.Append(sliceValue, reflect.Zero(elemType))
|
||||||
|
continue
|
||||||
|
}
|
||||||
dstValue := d.createDecodableValue(elemType)
|
dstValue := d.createDecodableValue(elemType)
|
||||||
if err := d.decodeValue(dstValue, v); err != nil {
|
if err := d.decodeValue(dstValue, v); err != nil {
|
||||||
return errors.Wrapf(err, "failed to decode value")
|
return errors.Wrapf(err, "failed to decode value")
|
||||||
|
|
@ -484,6 +504,15 @@ func (d *Decoder) decodeMap(dst reflect.Value, src ast.Node) error {
|
||||||
for mapIter.Next() {
|
for mapIter.Next() {
|
||||||
key := mapIter.Key()
|
key := mapIter.Key()
|
||||||
value := mapIter.Value()
|
value := mapIter.Value()
|
||||||
|
k := reflect.ValueOf(d.nodeToValue(key))
|
||||||
|
if k.IsValid() && k.Type().ConvertibleTo(keyType) {
|
||||||
|
k = k.Convert(keyType)
|
||||||
|
}
|
||||||
|
if valueType.Kind() == reflect.Ptr && value.Type() == ast.NullType {
|
||||||
|
// set nil value to pointer
|
||||||
|
mapValue.SetMapIndex(k, reflect.Zero(valueType))
|
||||||
|
continue
|
||||||
|
}
|
||||||
dstValue := d.createDecodableValue(valueType)
|
dstValue := d.createDecodableValue(valueType)
|
||||||
if err := d.decodeValue(dstValue, value); err != nil {
|
if err := d.decodeValue(dstValue, value); err != nil {
|
||||||
if xerrors.Is(err, errOverflowNumber) {
|
if xerrors.Is(err, errOverflowNumber) {
|
||||||
|
|
@ -492,8 +521,12 @@ func (d *Decoder) decodeMap(dst reflect.Value, src ast.Node) error {
|
||||||
}
|
}
|
||||||
return errors.Wrapf(err, "failed to decode value")
|
return errors.Wrapf(err, "failed to decode value")
|
||||||
}
|
}
|
||||||
castedKey := reflect.ValueOf(d.nodeToValue(key)).Convert(keyType)
|
if !k.IsValid() {
|
||||||
mapValue.SetMapIndex(castedKey, d.castToAssignableValue(dstValue, valueType))
|
// expect nil key
|
||||||
|
mapValue.SetMapIndex(d.createDecodableValue(keyType), d.castToAssignableValue(dstValue, valueType))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mapValue.SetMapIndex(k, d.castToAssignableValue(dstValue, valueType))
|
||||||
}
|
}
|
||||||
dst.Set(mapValue)
|
dst.Set(mapValue)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -279,6 +279,25 @@ func TestDecoder(t *testing.T) {
|
||||||
"a: 50cent_of_dollar",
|
"a: 50cent_of_dollar",
|
||||||
map[string]interface{}{"a": "50cent_of_dollar"},
|
map[string]interface{}{"a": "50cent_of_dollar"},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Nulls
|
||||||
|
{
|
||||||
|
"v:",
|
||||||
|
map[string]interface{}{"v": nil},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"v: ~",
|
||||||
|
map[string]interface{}{"v": nil},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"~: null key",
|
||||||
|
map[interface{}]string{nil: "null key"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"v:",
|
||||||
|
map[string]*bool{"v": nil},
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"v: .inf\n",
|
"v: .inf\n",
|
||||||
map[string]interface{}{"v": math.Inf(0)},
|
map[string]interface{}{"v": math.Inf(0)},
|
||||||
|
|
|
||||||
|
|
@ -173,10 +173,16 @@ func (p *Parser) parseMappingValue(ctx *Context) (ast.Node, error) {
|
||||||
ctx.progress(1) // progress to mapping value token
|
ctx.progress(1) // progress to mapping value token
|
||||||
tk := ctx.currentToken() // get mapping value token
|
tk := ctx.currentToken() // get mapping value token
|
||||||
ctx.progress(1) // progress to value token
|
ctx.progress(1) // progress to value token
|
||||||
value, err := p.parseToken(ctx, ctx.currentToken())
|
var value ast.Node
|
||||||
|
if vtk := ctx.currentToken(); vtk == nil {
|
||||||
|
value = ast.Null(token.New("null", "null", tk.Position))
|
||||||
|
} else {
|
||||||
|
v, err := p.parseToken(ctx, ctx.currentToken())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "failed to parse mapping 'value' node")
|
return nil, errors.Wrapf(err, "failed to parse mapping 'value' node")
|
||||||
}
|
}
|
||||||
|
value = v
|
||||||
|
}
|
||||||
keyColumn := key.GetToken().Position.Column
|
keyColumn := key.GetToken().Position.Column
|
||||||
valueColumn := value.GetToken().Position.Column
|
valueColumn := value.GetToken().Position.Column
|
||||||
if keyColumn == valueColumn {
|
if keyColumn == valueColumn {
|
||||||
|
|
@ -299,6 +305,9 @@ func (p *Parser) parseMapKey(tk *token.Token) ast.Node {
|
||||||
if tk.Type == token.MergeKeyType {
|
if tk.Type == token.MergeKeyType {
|
||||||
return ast.MergeKey(tk)
|
return ast.MergeKey(tk)
|
||||||
}
|
}
|
||||||
|
if tk.Type == token.NullType {
|
||||||
|
return ast.Null(tk)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,10 @@ func (c *Context) isEOS() bool {
|
||||||
return len(c.src)-1 <= c.idx
|
return len(c.src)-1 <= c.idx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Context) isNextEOS() bool {
|
||||||
|
return len(c.src)-1 <= c.idx+1
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Context) next() bool {
|
func (c *Context) next() bool {
|
||||||
return c.idx < c.size
|
return c.idx < c.size
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -364,7 +364,7 @@ func (s *Scanner) scan(ctx *Context) (pos int) {
|
||||||
return
|
return
|
||||||
case ':':
|
case ':':
|
||||||
nc := ctx.nextChar()
|
nc := ctx.nextChar()
|
||||||
if nc == ' ' || nc == '\n' {
|
if nc == ' ' || nc == '\n' || ctx.isNextEOS() {
|
||||||
// mapping value
|
// mapping value
|
||||||
tk := s.bufferedToken(ctx)
|
tk := s.bufferedToken(ctx)
|
||||||
if tk != nil {
|
if tk != nil {
|
||||||
|
|
|
||||||
|
|
@ -274,6 +274,8 @@ type ReservedKeyword string
|
||||||
const (
|
const (
|
||||||
// Null `null` keyword
|
// Null `null` keyword
|
||||||
Null ReservedKeyword = "null"
|
Null ReservedKeyword = "null"
|
||||||
|
// NullSymbol `~` keyword
|
||||||
|
NullSymbol = "~"
|
||||||
// False `false` keyword
|
// False `false` keyword
|
||||||
False = "false"
|
False = "false"
|
||||||
// True `true` keyword
|
// True `true` keyword
|
||||||
|
|
@ -299,6 +301,16 @@ var (
|
||||||
Position: pos,
|
Position: pos,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
NullSymbol: func(value, org string, pos *Position) *Token {
|
||||||
|
return &Token{
|
||||||
|
Type: NullType,
|
||||||
|
CharacterType: CharacterTypeMiscellaneous,
|
||||||
|
Indicator: NotIndicator,
|
||||||
|
Value: value,
|
||||||
|
Origin: org,
|
||||||
|
Position: pos,
|
||||||
|
}
|
||||||
|
},
|
||||||
False: func(value string, org string, pos *Position) *Token {
|
False: func(value string, org string, pos *Position) *Token {
|
||||||
return &Token{
|
return &Token{
|
||||||
Type: BoolType,
|
Type: BoolType,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue