Browse Source

codec: json: allow parsing non-string scalars where a string is expected

This allows us to parse valid json non-strings i.e. null, true, false or numbers
into a string correctly.

Also added TestJsonDecodeNonStringScalarInStringContext to validate it.

Fixes #202
Ugorji Nwoke 8 years ago
parent
commit
8c0409fcbb
2 changed files with 41 additions and 9 deletions
  1. 15 0
      codec/codec_test.go
  2. 26 9
      codec/json.go

+ 15 - 0
codec/codec_test.go

@@ -1470,6 +1470,21 @@ func TestJsonLargeInteger(t *testing.T) {
 	}
 	}
 }
 }
 
 
+func TestJsonDecodeNonStringScalarInStringContext(t *testing.T) {
+	var b = `{"s.true": "true", "b.true": true, "s.false": "false", "b.false": false, "s.10": "10", "i.10": 10, "i.-10": -10}`
+	var golden = map[string]string{"s.true": "true", "b.true": "true", "s.false": "false", "b.false": "false", "s.10": "10", "i.10": "10", "i.-10": "-10"}
+
+	var m map[string]string
+	d := NewDecoderBytes([]byte(b), testJsonH)
+	d.MustDecode(&m)
+	if err := deepEqual(golden, m); err == nil {
+		logT(t, "++++ match: decoded: %#v", m)
+	} else {
+		logT(t, "---- mismatch: %v ==> golden: %#v, decoded: %#v", err, golden, m)
+		failT(t)
+	}
+}
+
 // TODO:
 // TODO:
 //   Add Tests for:
 //   Add Tests for:
 //   - decoding empty list/map in stream into a nil slice/map
 //   - decoding empty list/map in stream into a nil slice/map

+ 26 - 9
codec/json.go

@@ -702,7 +702,9 @@ LOOP:
 			switch state {
 			switch state {
 			case 0:
 			case 0:
 				state = 2
 				state = 2
-				// do not add sign to the slice ...
+				if storeBytes {
+					d.bs = append(d.bs, b)
+				}
 				b, eof = r.readn1eof()
 				b, eof = r.readn1eof()
 				continue
 				continue
 			case 6: // typ = jsonNumFloat
 			case 6: // typ = jsonNumFloat
@@ -715,7 +717,9 @@ LOOP:
 			case 0:
 			case 0:
 				state = 2
 				state = 2
 				n.neg = true
 				n.neg = true
-				// do not add sign to the slice ...
+				if storeBytes {
+					d.bs = append(d.bs, b)
+				}
 				b, eof = r.readn1eof()
 				b, eof = r.readn1eof()
 				continue
 				continue
 			case 6: // typ = jsonNumFloat
 			case 6: // typ = jsonNumFloat
@@ -981,16 +985,28 @@ func (d *jsonDecDriver) appendStringAsBytes() {
 		d.tok = b
 		d.tok = b
 	}
 	}
 
 
-	// handle null as a string
-	if d.tok == 'n' {
-		d.readStrIdx(10, 13) // ull
-		d.bs = d.bs[:0]
+	if d.tok != '"' {
+		// d.d.errorf("json: expect char '%c' but got char '%c'", '"', d.tok)
+		// handle non-string scalar: null, true, false or a number
+		switch d.tok {
+		case 'n':
+			d.readStrIdx(10, 13) // ull
+			d.bs = d.bs[:0]
+		case 'f':
+			d.readStrIdx(5, 9) // alse
+			d.bs = d.bs[:5]
+			copy(d.bs, "false")
+		case 't':
+			d.readStrIdx(1, 4) // rue
+			d.bs = d.bs[:4]
+			copy(d.bs, "true")
+		default:
+			// try to parse a valid number
+			d.decNum(true)
+		}
 		return
 		return
 	}
 	}
 
 
-	if d.tok != '"' {
-		d.d.errorf("json: expect char '%c' but got char '%c'", '"', d.tok)
-	}
 	d.tok = 0
 	d.tok = 0
 
 
 	v := d.bs[:0]
 	v := d.bs[:0]
@@ -1159,6 +1175,7 @@ func (d *jsonDecDriver) DecodeNaked() {
 type JsonHandle struct {
 type JsonHandle struct {
 	textEncodingType
 	textEncodingType
 	BasicHandle
 	BasicHandle
+
 	// RawBytesExt, if configured, is used to encode and decode raw bytes in a custom way.
 	// RawBytesExt, if configured, is used to encode and decode raw bytes in a custom way.
 	// If not configured, raw bytes are encoded to/from base64 text.
 	// If not configured, raw bytes are encoded to/from base64 text.
 	RawBytesExt InterfaceExt
 	RawBytesExt InterfaceExt