mirror of
https://github.com/goccy/go-yaml.git
synced 2025-11-01 22:01: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() {
|
||||
return nil
|
||||
}
|
||||
if src.Type() == ast.NullType {
|
||||
// set nil value to pointer
|
||||
dst.Set(reflect.Zero(valueType))
|
||||
return nil
|
||||
}
|
||||
v := d.createDecodableValue(dst.Type())
|
||||
if err := d.decodeValue(v, src); err != nil {
|
||||
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() {
|
||||
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())
|
||||
if err := d.decodeValue(newFieldValue, src); err != nil {
|
||||
if xerrors.Is(err, errOverflowNumber) {
|
||||
|
|
@ -416,6 +426,11 @@ func (d *Decoder) decodeStruct(dst reflect.Value, src ast.Node) error {
|
|||
continue
|
||||
}
|
||||
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())
|
||||
if err := d.decodeValue(newFieldValue, v); err != nil {
|
||||
if xerrors.Is(err, errOverflowNumber) {
|
||||
|
|
@ -461,6 +476,11 @@ func (d *Decoder) decodeSlice(dst reflect.Value, src ast.Node) error {
|
|||
elemType := sliceType.Elem()
|
||||
for iter.Next() {
|
||||
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)
|
||||
if err := d.decodeValue(dstValue, v); err != nil {
|
||||
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() {
|
||||
key := mapIter.Key()
|
||||
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)
|
||||
if err := d.decodeValue(dstValue, value); err != nil {
|
||||
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")
|
||||
}
|
||||
castedKey := reflect.ValueOf(d.nodeToValue(key)).Convert(keyType)
|
||||
mapValue.SetMapIndex(castedKey, d.castToAssignableValue(dstValue, valueType))
|
||||
if !k.IsValid() {
|
||||
// expect nil key
|
||||
mapValue.SetMapIndex(d.createDecodableValue(keyType), d.castToAssignableValue(dstValue, valueType))
|
||||
continue
|
||||
}
|
||||
mapValue.SetMapIndex(k, d.castToAssignableValue(dstValue, valueType))
|
||||
}
|
||||
dst.Set(mapValue)
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -279,6 +279,25 @@ func TestDecoder(t *testing.T) {
|
|||
"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",
|
||||
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
|
||||
tk := ctx.currentToken() // get mapping 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 {
|
||||
return nil, errors.Wrapf(err, "failed to parse mapping 'value' node")
|
||||
}
|
||||
value = v
|
||||
}
|
||||
keyColumn := key.GetToken().Position.Column
|
||||
valueColumn := value.GetToken().Position.Column
|
||||
if keyColumn == valueColumn {
|
||||
|
|
@ -299,6 +305,9 @@ func (p *Parser) parseMapKey(tk *token.Token) ast.Node {
|
|||
if tk.Type == token.MergeKeyType {
|
||||
return ast.MergeKey(tk)
|
||||
}
|
||||
if tk.Type == token.NullType {
|
||||
return ast.Null(tk)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,10 @@ func (c *Context) isEOS() bool {
|
|||
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 {
|
||||
return c.idx < c.size
|
||||
}
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ func (s *Scanner) scan(ctx *Context) (pos int) {
|
|||
return
|
||||
case ':':
|
||||
nc := ctx.nextChar()
|
||||
if nc == ' ' || nc == '\n' {
|
||||
if nc == ' ' || nc == '\n' || ctx.isNextEOS() {
|
||||
// mapping value
|
||||
tk := s.bufferedToken(ctx)
|
||||
if tk != nil {
|
||||
|
|
|
|||
|
|
@ -274,6 +274,8 @@ type ReservedKeyword string
|
|||
const (
|
||||
// Null `null` keyword
|
||||
Null ReservedKeyword = "null"
|
||||
// NullSymbol `~` keyword
|
||||
NullSymbol = "~"
|
||||
// False `false` keyword
|
||||
False = "false"
|
||||
// True `true` keyword
|
||||
|
|
@ -299,6 +301,16 @@ var (
|
|||
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 {
|
||||
return &Token{
|
||||
Type: BoolType,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue