json_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. package apijson
  2. import (
  3. "reflect"
  4. "strings"
  5. "testing"
  6. "time"
  7. "github.com/tidwall/gjson"
  8. )
  9. func P[T any](v T) *T { return &v }
  10. type Primitives struct {
  11. A bool `json:"a"`
  12. B int `json:"b"`
  13. C uint `json:"c"`
  14. D float64 `json:"d"`
  15. E float32 `json:"e"`
  16. F []int `json:"f"`
  17. }
  18. type PrimitivePointers struct {
  19. A *bool `json:"a"`
  20. B *int `json:"b"`
  21. C *uint `json:"c"`
  22. D *float64 `json:"d"`
  23. E *float32 `json:"e"`
  24. F *[]int `json:"f"`
  25. }
  26. type Slices struct {
  27. Slice []Primitives `json:"slices"`
  28. }
  29. type DateTime struct {
  30. Date time.Time `json:"date" format:"date"`
  31. DateTime time.Time `json:"date-time" format:"date-time"`
  32. }
  33. type AdditionalProperties struct {
  34. A bool `json:"a"`
  35. ExtraFields map[string]interface{} `json:"-,extras"`
  36. }
  37. type TypedAdditionalProperties struct {
  38. A bool `json:"a"`
  39. ExtraFields map[string]int `json:"-,extras"`
  40. }
  41. type EmbeddedStruct struct {
  42. A bool `json:"a"`
  43. B string `json:"b"`
  44. JSON EmbeddedStructJSON
  45. }
  46. type EmbeddedStructJSON struct {
  47. A Field
  48. B Field
  49. ExtraFields map[string]Field
  50. raw string
  51. }
  52. type EmbeddedStructs struct {
  53. EmbeddedStruct
  54. A *int `json:"a"`
  55. ExtraFields map[string]interface{} `json:"-,extras"`
  56. JSON EmbeddedStructsJSON
  57. }
  58. type EmbeddedStructsJSON struct {
  59. A Field
  60. ExtraFields map[string]Field
  61. raw string
  62. }
  63. type Recursive struct {
  64. Name string `json:"name"`
  65. Child *Recursive `json:"child"`
  66. }
  67. type JSONFieldStruct struct {
  68. A bool `json:"a"`
  69. B int64 `json:"b"`
  70. C string `json:"c"`
  71. D string `json:"d"`
  72. ExtraFields map[string]int64 `json:"-,extras"`
  73. JSON JSONFieldStructJSON `json:"-,metadata"`
  74. }
  75. type JSONFieldStructJSON struct {
  76. A Field
  77. B Field
  78. C Field
  79. D Field
  80. ExtraFields map[string]Field
  81. raw string
  82. }
  83. type UnknownStruct struct {
  84. Unknown interface{} `json:"unknown"`
  85. }
  86. type UnionStruct struct {
  87. Union Union `json:"union" format:"date"`
  88. }
  89. type Union interface {
  90. union()
  91. }
  92. type Inline struct {
  93. InlineField Primitives `json:"-,inline"`
  94. JSON InlineJSON `json:"-,metadata"`
  95. }
  96. type InlineArray struct {
  97. InlineField []string `json:"-,inline"`
  98. JSON InlineJSON `json:"-,metadata"`
  99. }
  100. type InlineJSON struct {
  101. InlineField Field
  102. raw string
  103. }
  104. type UnionInteger int64
  105. func (UnionInteger) union() {}
  106. type UnionStructA struct {
  107. Type string `json:"type"`
  108. A string `json:"a"`
  109. B string `json:"b"`
  110. }
  111. func (UnionStructA) union() {}
  112. type UnionStructB struct {
  113. Type string `json:"type"`
  114. A string `json:"a"`
  115. }
  116. func (UnionStructB) union() {}
  117. type UnionTime time.Time
  118. func (UnionTime) union() {}
  119. func init() {
  120. RegisterUnion(reflect.TypeOf((*Union)(nil)).Elem(), "type",
  121. UnionVariant{
  122. TypeFilter: gjson.String,
  123. Type: reflect.TypeOf(UnionTime{}),
  124. },
  125. UnionVariant{
  126. TypeFilter: gjson.Number,
  127. Type: reflect.TypeOf(UnionInteger(0)),
  128. },
  129. UnionVariant{
  130. TypeFilter: gjson.JSON,
  131. DiscriminatorValue: "typeA",
  132. Type: reflect.TypeOf(UnionStructA{}),
  133. },
  134. UnionVariant{
  135. TypeFilter: gjson.JSON,
  136. DiscriminatorValue: "typeB",
  137. Type: reflect.TypeOf(UnionStructB{}),
  138. },
  139. )
  140. }
  141. type ComplexUnionStruct struct {
  142. Union ComplexUnion `json:"union"`
  143. }
  144. type ComplexUnion interface {
  145. complexUnion()
  146. }
  147. type ComplexUnionA struct {
  148. Boo string `json:"boo"`
  149. Foo bool `json:"foo"`
  150. }
  151. func (ComplexUnionA) complexUnion() {}
  152. type ComplexUnionB struct {
  153. Boo bool `json:"boo"`
  154. Foo string `json:"foo"`
  155. }
  156. func (ComplexUnionB) complexUnion() {}
  157. type ComplexUnionC struct {
  158. Boo int64 `json:"boo"`
  159. }
  160. func (ComplexUnionC) complexUnion() {}
  161. type ComplexUnionTypeA struct {
  162. Baz int64 `json:"baz"`
  163. Type TypeA `json:"type"`
  164. }
  165. func (ComplexUnionTypeA) complexUnion() {}
  166. type TypeA string
  167. func (t TypeA) IsKnown() bool {
  168. return t == "a"
  169. }
  170. type ComplexUnionTypeB struct {
  171. Baz int64 `json:"baz"`
  172. Type TypeB `json:"type"`
  173. }
  174. type TypeB string
  175. func (t TypeB) IsKnown() bool {
  176. return t == "b"
  177. }
  178. type UnmarshalStruct struct {
  179. Foo string `json:"foo"`
  180. prop bool `json:"-"`
  181. }
  182. func (r *UnmarshalStruct) UnmarshalJSON(json []byte) error {
  183. r.prop = true
  184. return UnmarshalRoot(json, r)
  185. }
  186. func (ComplexUnionTypeB) complexUnion() {}
  187. func init() {
  188. RegisterUnion(reflect.TypeOf((*ComplexUnion)(nil)).Elem(), "",
  189. UnionVariant{
  190. TypeFilter: gjson.JSON,
  191. Type: reflect.TypeOf(ComplexUnionA{}),
  192. },
  193. UnionVariant{
  194. TypeFilter: gjson.JSON,
  195. Type: reflect.TypeOf(ComplexUnionB{}),
  196. },
  197. UnionVariant{
  198. TypeFilter: gjson.JSON,
  199. Type: reflect.TypeOf(ComplexUnionC{}),
  200. },
  201. UnionVariant{
  202. TypeFilter: gjson.JSON,
  203. Type: reflect.TypeOf(ComplexUnionTypeA{}),
  204. },
  205. UnionVariant{
  206. TypeFilter: gjson.JSON,
  207. Type: reflect.TypeOf(ComplexUnionTypeB{}),
  208. },
  209. )
  210. }
  211. type MarshallingUnionStruct struct {
  212. Union MarshallingUnion
  213. }
  214. func (r *MarshallingUnionStruct) UnmarshalJSON(data []byte) (err error) {
  215. *r = MarshallingUnionStruct{}
  216. err = UnmarshalRoot(data, &r.Union)
  217. return
  218. }
  219. func (r MarshallingUnionStruct) MarshalJSON() (data []byte, err error) {
  220. return MarshalRoot(r.Union)
  221. }
  222. type MarshallingUnion interface {
  223. marshallingUnion()
  224. }
  225. type MarshallingUnionA struct {
  226. Boo string `json:"boo"`
  227. }
  228. func (MarshallingUnionA) marshallingUnion() {}
  229. func (r *MarshallingUnionA) UnmarshalJSON(data []byte) (err error) {
  230. return UnmarshalRoot(data, r)
  231. }
  232. type MarshallingUnionB struct {
  233. Foo string `json:"foo"`
  234. }
  235. func (MarshallingUnionB) marshallingUnion() {}
  236. func (r *MarshallingUnionB) UnmarshalJSON(data []byte) (err error) {
  237. return UnmarshalRoot(data, r)
  238. }
  239. func init() {
  240. RegisterUnion(
  241. reflect.TypeOf((*MarshallingUnion)(nil)).Elem(),
  242. "",
  243. UnionVariant{
  244. TypeFilter: gjson.JSON,
  245. Type: reflect.TypeOf(MarshallingUnionA{}),
  246. },
  247. UnionVariant{
  248. TypeFilter: gjson.JSON,
  249. Type: reflect.TypeOf(MarshallingUnionB{}),
  250. },
  251. )
  252. }
  253. var tests = map[string]struct {
  254. buf string
  255. val interface{}
  256. }{
  257. "true": {"true", true},
  258. "false": {"false", false},
  259. "int": {"1", 1},
  260. "int_bigger": {"12324", 12324},
  261. "int_string_coerce": {`"65"`, 65},
  262. "int_boolean_coerce": {"true", 1},
  263. "int64": {"1", int64(1)},
  264. "int64_huge": {"123456789123456789", int64(123456789123456789)},
  265. "uint": {"1", uint(1)},
  266. "uint_bigger": {"12324", uint(12324)},
  267. "uint_coerce": {`"65"`, uint(65)},
  268. "float_1.54": {"1.54", float32(1.54)},
  269. "float_1.89": {"1.89", float64(1.89)},
  270. "string": {`"str"`, "str"},
  271. "string_int_coerce": {`12`, "12"},
  272. "array_string": {`["foo","bar"]`, []string{"foo", "bar"}},
  273. "array_int": {`[1,2]`, []int{1, 2}},
  274. "array_int_coerce": {`["1",2]`, []int{1, 2}},
  275. "ptr_true": {"true", P(true)},
  276. "ptr_false": {"false", P(false)},
  277. "ptr_int": {"1", P(1)},
  278. "ptr_int_bigger": {"12324", P(12324)},
  279. "ptr_int_string_coerce": {`"65"`, P(65)},
  280. "ptr_int_boolean_coerce": {"true", P(1)},
  281. "ptr_int64": {"1", P(int64(1))},
  282. "ptr_int64_huge": {"123456789123456789", P(int64(123456789123456789))},
  283. "ptr_uint": {"1", P(uint(1))},
  284. "ptr_uint_bigger": {"12324", P(uint(12324))},
  285. "ptr_uint_coerce": {`"65"`, P(uint(65))},
  286. "ptr_float_1.54": {"1.54", P(float32(1.54))},
  287. "ptr_float_1.89": {"1.89", P(float64(1.89))},
  288. "date_time": {`"2007-03-01T13:00:00Z"`, time.Date(2007, time.March, 1, 13, 0, 0, 0, time.UTC)},
  289. "date_time_nano_coerce": {`"2007-03-01T13:03:05.123456789Z"`, time.Date(2007, time.March, 1, 13, 3, 5, 123456789, time.UTC)},
  290. "date_time_missing_t_coerce": {`"2007-03-01 13:03:05Z"`, time.Date(2007, time.March, 1, 13, 3, 5, 0, time.UTC)},
  291. "date_time_missing_timezone_coerce": {`"2007-03-01T13:03:05"`, time.Date(2007, time.March, 1, 13, 3, 5, 0, time.UTC)},
  292. // note: using -1200 to minimize probability of conflicting with the local timezone of the test runner
  293. // see https://en.wikipedia.org/wiki/UTC%E2%88%9212:00
  294. "date_time_missing_timezone_colon_coerce": {`"2007-03-01T13:03:05-1200"`, time.Date(2007, time.March, 1, 13, 3, 5, 0, time.FixedZone("", -12*60*60))},
  295. "date_time_nano_missing_t_coerce": {`"2007-03-01 13:03:05.123456789Z"`, time.Date(2007, time.March, 1, 13, 3, 5, 123456789, time.UTC)},
  296. "map_string": {`{"foo":"bar"}`, map[string]string{"foo": "bar"}},
  297. "map_string_with_sjson_path_chars": {`{":a.b.c*:d*-1e.f":"bar"}`, map[string]string{":a.b.c*:d*-1e.f": "bar"}},
  298. "map_interface": {`{"a":1,"b":"str","c":false}`, map[string]interface{}{"a": float64(1), "b": "str", "c": false}},
  299. "primitive_struct": {
  300. `{"a":false,"b":237628372683,"c":654,"d":9999.43,"e":43.76,"f":[1,2,3,4]}`,
  301. Primitives{A: false, B: 237628372683, C: uint(654), D: 9999.43, E: 43.76, F: []int{1, 2, 3, 4}},
  302. },
  303. "slices": {
  304. `{"slices":[{"a":false,"b":237628372683,"c":654,"d":9999.43,"e":43.76,"f":[1,2,3,4]}]}`,
  305. Slices{
  306. Slice: []Primitives{{A: false, B: 237628372683, C: uint(654), D: 9999.43, E: 43.76, F: []int{1, 2, 3, 4}}},
  307. },
  308. },
  309. "primitive_pointer_struct": {
  310. `{"a":false,"b":237628372683,"c":654,"d":9999.43,"e":43.76,"f":[1,2,3,4,5]}`,
  311. PrimitivePointers{
  312. A: P(false),
  313. B: P(237628372683),
  314. C: P(uint(654)),
  315. D: P(9999.43),
  316. E: P(float32(43.76)),
  317. F: &[]int{1, 2, 3, 4, 5},
  318. },
  319. },
  320. "datetime_struct": {
  321. `{"date":"2006-01-02","date-time":"2006-01-02T15:04:05Z"}`,
  322. DateTime{
  323. Date: time.Date(2006, time.January, 2, 0, 0, 0, 0, time.UTC),
  324. DateTime: time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC),
  325. },
  326. },
  327. "additional_properties": {
  328. `{"a":true,"bar":"value","foo":true}`,
  329. AdditionalProperties{
  330. A: true,
  331. ExtraFields: map[string]interface{}{
  332. "bar": "value",
  333. "foo": true,
  334. },
  335. },
  336. },
  337. "embedded_struct": {
  338. `{"a":1,"b":"bar"}`,
  339. EmbeddedStructs{
  340. EmbeddedStruct: EmbeddedStruct{
  341. A: true,
  342. B: "bar",
  343. JSON: EmbeddedStructJSON{
  344. A: Field{raw: `1`, status: valid},
  345. B: Field{raw: `"bar"`, status: valid},
  346. raw: `{"a":1,"b":"bar"}`,
  347. },
  348. },
  349. A: P(1),
  350. ExtraFields: map[string]interface{}{"b": "bar"},
  351. JSON: EmbeddedStructsJSON{
  352. A: Field{raw: `1`, status: valid},
  353. ExtraFields: map[string]Field{
  354. "b": {raw: `"bar"`, status: valid},
  355. },
  356. raw: `{"a":1,"b":"bar"}`,
  357. },
  358. },
  359. },
  360. "recursive_struct": {
  361. `{"child":{"name":"Alex"},"name":"Robert"}`,
  362. Recursive{Name: "Robert", Child: &Recursive{Name: "Alex"}},
  363. },
  364. "metadata_coerce": {
  365. `{"a":"12","b":"12","c":null,"extra_typed":12,"extra_untyped":{"foo":"bar"}}`,
  366. JSONFieldStruct{
  367. A: false,
  368. B: 12,
  369. C: "",
  370. JSON: JSONFieldStructJSON{
  371. raw: `{"a":"12","b":"12","c":null,"extra_typed":12,"extra_untyped":{"foo":"bar"}}`,
  372. A: Field{raw: `"12"`, status: invalid},
  373. B: Field{raw: `"12"`, status: valid},
  374. C: Field{raw: "null", status: null},
  375. D: Field{raw: "", status: missing},
  376. ExtraFields: map[string]Field{
  377. "extra_typed": {
  378. raw: "12",
  379. status: valid,
  380. },
  381. "extra_untyped": {
  382. raw: `{"foo":"bar"}`,
  383. status: invalid,
  384. },
  385. },
  386. },
  387. ExtraFields: map[string]int64{
  388. "extra_typed": 12,
  389. "extra_untyped": 0,
  390. },
  391. },
  392. },
  393. "unknown_struct_number": {
  394. `{"unknown":12}`,
  395. UnknownStruct{
  396. Unknown: 12.,
  397. },
  398. },
  399. "unknown_struct_map": {
  400. `{"unknown":{"foo":"bar"}}`,
  401. UnknownStruct{
  402. Unknown: map[string]interface{}{
  403. "foo": "bar",
  404. },
  405. },
  406. },
  407. "union_integer": {
  408. `{"union":12}`,
  409. UnionStruct{
  410. Union: UnionInteger(12),
  411. },
  412. },
  413. "union_struct_discriminated_a": {
  414. `{"union":{"a":"foo","b":"bar","type":"typeA"}}`,
  415. UnionStruct{
  416. Union: UnionStructA{
  417. Type: "typeA",
  418. A: "foo",
  419. B: "bar",
  420. },
  421. },
  422. },
  423. "union_struct_discriminated_b": {
  424. `{"union":{"a":"foo","type":"typeB"}}`,
  425. UnionStruct{
  426. Union: UnionStructB{
  427. Type: "typeB",
  428. A: "foo",
  429. },
  430. },
  431. },
  432. "union_struct_time": {
  433. `{"union":"2010-05-23"}`,
  434. UnionStruct{
  435. Union: UnionTime(time.Date(2010, 05, 23, 0, 0, 0, 0, time.UTC)),
  436. },
  437. },
  438. "complex_union_a": {
  439. `{"union":{"boo":"12","foo":true}}`,
  440. ComplexUnionStruct{Union: ComplexUnionA{Boo: "12", Foo: true}},
  441. },
  442. "complex_union_b": {
  443. `{"union":{"boo":true,"foo":"12"}}`,
  444. ComplexUnionStruct{Union: ComplexUnionB{Boo: true, Foo: "12"}},
  445. },
  446. "complex_union_c": {
  447. `{"union":{"boo":12}}`,
  448. ComplexUnionStruct{Union: ComplexUnionC{Boo: 12}},
  449. },
  450. "complex_union_type_a": {
  451. `{"union":{"baz":12,"type":"a"}}`,
  452. ComplexUnionStruct{Union: ComplexUnionTypeA{Baz: 12, Type: TypeA("a")}},
  453. },
  454. "complex_union_type_b": {
  455. `{"union":{"baz":12,"type":"b"}}`,
  456. ComplexUnionStruct{Union: ComplexUnionTypeB{Baz: 12, Type: TypeB("b")}},
  457. },
  458. "marshalling_union_a": {
  459. `{"boo":"hello"}`,
  460. MarshallingUnionStruct{Union: MarshallingUnionA{Boo: "hello"}},
  461. },
  462. "marshalling_union_b": {
  463. `{"foo":"hi"}`,
  464. MarshallingUnionStruct{Union: MarshallingUnionB{Foo: "hi"}},
  465. },
  466. "unmarshal": {
  467. `{"foo":"hello"}`,
  468. &UnmarshalStruct{Foo: "hello", prop: true},
  469. },
  470. "array_of_unmarshal": {
  471. `[{"foo":"hello"}]`,
  472. []UnmarshalStruct{{Foo: "hello", prop: true}},
  473. },
  474. "inline_coerce": {
  475. `{"a":false,"b":237628372683,"c":654,"d":9999.43,"e":43.76,"f":[1,2,3,4]}`,
  476. Inline{
  477. InlineField: Primitives{A: false, B: 237628372683, C: 0x28e, D: 9999.43, E: 43.76, F: []int{1, 2, 3, 4}},
  478. JSON: InlineJSON{
  479. InlineField: Field{raw: "{\"a\":false,\"b\":237628372683,\"c\":654,\"d\":9999.43,\"e\":43.76,\"f\":[1,2,3,4]}", status: 3},
  480. raw: "{\"a\":false,\"b\":237628372683,\"c\":654,\"d\":9999.43,\"e\":43.76,\"f\":[1,2,3,4]}",
  481. },
  482. },
  483. },
  484. "inline_array_coerce": {
  485. `["Hello","foo","bar"]`,
  486. InlineArray{
  487. InlineField: []string{"Hello", "foo", "bar"},
  488. JSON: InlineJSON{
  489. InlineField: Field{raw: `["Hello","foo","bar"]`, status: 3},
  490. raw: `["Hello","foo","bar"]`,
  491. },
  492. },
  493. },
  494. }
  495. func TestDecode(t *testing.T) {
  496. for name, test := range tests {
  497. t.Run(name, func(t *testing.T) {
  498. result := reflect.New(reflect.TypeOf(test.val))
  499. if err := Unmarshal([]byte(test.buf), result.Interface()); err != nil {
  500. t.Fatalf("deserialization of %v failed with error %v", result, err)
  501. }
  502. if !reflect.DeepEqual(result.Elem().Interface(), test.val) {
  503. t.Fatalf("expected '%s' to deserialize to \n%#v\nbut got\n%#v", test.buf, test.val, result.Elem().Interface())
  504. }
  505. })
  506. }
  507. }
  508. func TestEncode(t *testing.T) {
  509. for name, test := range tests {
  510. if strings.HasSuffix(name, "_coerce") {
  511. continue
  512. }
  513. t.Run(name, func(t *testing.T) {
  514. raw, err := Marshal(test.val)
  515. if err != nil {
  516. t.Fatalf("serialization of %v failed with error %v", test.val, err)
  517. }
  518. if string(raw) != test.buf {
  519. t.Fatalf("expected %+#v to serialize to %s but got %s", test.val, test.buf, string(raw))
  520. }
  521. })
  522. }
  523. }