go 在解码时间时,将字段名包含在解码错误消息中,

qoefvg9y  于 4个月前  发布在  Go
关注(0)|答案(4)|浏览(106)

#6716分配。

您正在使用的Go版本是什么(go version)?

go1.9.2 linux/amd64

这个问题在最新版本中是否会重现?

是的

您正在使用什么操作系统和处理器架构(go env)?

GOARCH="amd64"
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"

您做了什么?

运行https://play.golang.org/p/YnlDi-3DMP
如果可能,请提供一个重现错误的食谱。
一个完整的可运行程序很好。
play.golang.org上的链接是最好的。

您期望看到什么?

带有错误信息的字段名

您实际上看到了什么?

没有字段名的错误

jhkqcmku

jhkqcmku1#

在这里,我希望看到的是json.Unmarshal返回的错误具有一致性。例如,始终返回一个json.UnmarshalTypeError,该错误可能还包含对底层错误的引用以及与错误条件相关的现有值、类型、偏移量、结构和字段信息。这样做,而不是仅仅将底层错误向上冒泡(在这种情况下是一个time.ParseError),可以使处理解组错误更加一致。

最近在一个项目中,我确实需要这样做,所以我复制了encoding/json包,并在decoder.go中做了以下更改:

加号表示添加,减号表示减少。

// An UnmarshalTypeError describes a JSON value that was
// not appropriate for a value of a specific Go type.
type UnmarshalTypeError struct {
	Value  string       // description of JSON value - "bool", "array", "number -5"
	Type   reflect.Type // type of Go value it could not be assigned to
	Offset int64        // error occurred after reading Offset bytes
	Struct string       // name of the struct type containing the field
	Field  string       // name of the field holding the Go value
+	Err    error        // error, if any which occurred during unmarshal
}
// literalStore decodes a literal stored in item into v.
//
// fromQuoted indicates whether this literal came from unwrapping a
// string from the ",string" struct tag option. this is used only to
// produce more helpful error messages.
func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) {
	// Check for unmarshaler.
	if len(item) == 0 {
		//Empty string given
		d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
		return
	}
	isNull := item[0] == 'n' // null
	u, ut, pv := d.indirect(v, isNull)
	if u != nil {
		err := u.UnmarshalJSON(item)
		if err != nil {
-                      d.error(err)
+			var val string
+			switch item[0] {
+			case '"':
+				val = "string"
+			case 'n':
+				val = "null"
+			case 't', 'f':
+				val = "bool"
+			default:
+				val = "number"
+			}
+			d.error(&UnmarshalTypeError{Value: val, Type: v.Type(), Offset: int64(d.off), Err: err})
		}
		return
	}

...

尽管如此,我只在测试中做了几个小改动,就让它们全部通过了。公平地说,我不确定我已经覆盖了所有可能的代码路径。

1u4esq0p

1u4esq0p2#

这是向数组函数中添加的几行代码,用于为数组添加字段上下文。
第一列为加号,表示添加的内容

// array consumes an array from d.data[d.off-1:], decoding into the value v.
// the first byte of the array ('[') has been read already.
func (d *decodeState) array(v reflect.Value) {
+	arrayField := d.errorContext.Field
	// Check for unmarshaler.
	u, ut, pv := d.indirect(v, false)
	if u != nil {
		d.off--
		err := u.UnmarshalJSON(d.next())
		if err != nil {
			d.error(err)
		}
		return
	}
	if ut != nil {
		d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off)})
		d.off--
		d.next()
		return
	}

	v = pv

	// Check type of target.
	switch v.Kind() {
	case reflect.Interface:
		if v.NumMethod() == 0 {
			// Decoding into nil interface?  Switch to non-reflect code.
			v.Set(reflect.ValueOf(d.arrayInterface()))
			return
		}
		// Otherwise it's invalid.
		fallthrough
	default:
		d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off)})
		d.off--
		d.next()
		return
	case reflect.Array:
	case reflect.Slice:
		break
	}

	i := 0
	for {
+		d.errorContext.Field = fmt.Sprintf("%s[%v]", arrayField, i)
		// Look ahead for ] - can only happen on first iteration.
		op := d.scanWhile(scanSkipSpace)
		if op == scanEndArray {
			break
		}

		// Back up so d.value can have the byte we just read.
		d.off--
		d.scan.undo(op)

		// Get element of array, growing if necessary.
		if v.Kind() == reflect.Slice {
			// Grow slice if necessary
			if i >= v.Cap() {
				newcap := v.Cap() + v.Cap()/2
				if newcap < 4 {
					newcap = 4
				}
				newv := reflect.MakeSlice(v.Type(), v.Len(), newcap)
				reflect.Copy(newv, v)
				v.Set(newv)
			}
			if i >= v.Len() {
				v.SetLen(i + 1)
			}
		}

		if i < v.Len() {
			// Decode into element.
			d.value(v.Index(i))
		} else {
			// Ran out of fixed array: skip.
			d.value(reflect.Value{})
		}
		i++

		// Next token must be , or ].
		op = d.scanWhile(scanSkipSpace)
		if op == scanEndArray {
			break
		}
		if op != scanArrayValue {
			d.error(errPhase)
		}
	}

	if i < v.Len() {
		if v.Kind() == reflect.Array {
			// Array. Zero the rest.
			z := reflect.Zero(v.Type().Elem())
			for ; i < v.Len(); i++ {
				v.Index(i).Set(z)
			}
		} else {
			v.SetLen(i)
		}
	}
	if i == 0 && v.Kind() == reflect.Slice {
		v.Set(reflect.MakeSlice(v.Type(), 0, 0))
	}

+	d.errorContext.Field = arrayField

}
unftdfkk

unftdfkk3#

这是为数组函数添加的几行代码,用于为数组添加字段上下文。
列0中的加号表示添加

// array consumes an array from d.data[d.off-1:], decoding into the value v.
// the first byte of the array ('[') has been read already.
func (d *decodeState) array(v reflect.Value) {
+	arrayField := d.errorContext.Field
	// Check for unmarshaler.
	u, ut, pv := d.indirect(v, false)
	if u != nil {
		d.off--
		err := u.UnmarshalJSON(d.next())
		if err != nil {
			d.error(err)
		}
		return
	}
	if ut != nil {
		d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off)})
		d.off--
		d.next()
		return
	}

	v = pv

	// Check type of target.
	switch v.Kind() {
	case reflect.Interface:
		if v.NumMethod() == 0 {
			// Decoding into nil interface?  Switch to non-reflect code.
			v.Set(reflect.ValueOf(d.arrayInterface()))
			return
		}
		// Otherwise it's invalid.
		fallthrough
	default:
		d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off)})
		d.off--
		d.next()
		return
	case reflect.Array:
	case reflect.Slice:
		break
	}

	i := 0
	for {
+		d.errorContext.Field = fmt.Sprintf("%s[%v]", arrayField, i)
		// Look ahead for ] - can only happen on first iteration.
		op := d.scanWhile(scanSkipSpace)
		if op == scanEndArray {
			break
		}

		// Back up so d.value can have the byte we just read.
		d.off--
		d.scan.undo(op)

		// Get element of array, growing if necessary.
		if v.Kind() == reflect.Slice {
			// Grow slice if necessary
			if i >= v.Cap() {
				newcap := v.Cap() + v.Cap()/2
				if newcap < 4 {
					newcap = 4
				}
				newv := reflect.MakeSlice(v.Type(), v.Len(), newcap)
				reflect.Copy(newv, v)
				v.Set(newv)
			}
			if i >= v.Len() {
				v.SetLen(i + 1)
			}
		}

		if i < v.Len() {
			// Decode into element.
			d.value(v.Index(i))
		} else {
			// Ran out of fixed array: skip.
			d.value(reflect.Value{})
		}
		i++

		// Next token must be , or ].
		op = d.scanWhile(scanSkipSpace)
		if op == scanEndArray {
			break
		}
		if op != scanArrayValue {
			d.error(errPhase)
		}
	}

	if i < v.Len() {
		if v.Kind() == reflect.Array {
			// Array. Zero the rest.
			z := reflect.Zero(v.Type().Elem())
			for ; i < v.Len(); i++ {
				v.Index(i).Set(z)
			}
		} else {
			v.SetLen(i)
		}
	}
	if i == 0 && v.Kind() == reflect.Slice {
		v.Set(reflect.MakeSlice(v.Type(), 0, 0))
	}

+	d.errorContext.Field = arrayField

}

你是否尝试提交PR以进行此更改?

hs1rzwqc

hs1rzwqc4#

我可以为这个问题提交一个PR。

相关问题