Best Go-testdeep code snippet using td.gotViaJSON
td_json.go
Source:td_json.go
...359 baseOKNil360 expected reflect.Value361}362var _ TestDeep = &tdJSON{}363func gotViaJSON(ctx ctxerr.Context, pGot *reflect.Value) *ctxerr.Error {364 got, err := jsonify(ctx, *pGot)365 if err != nil {366 return err367 }368 *pGot = reflect.ValueOf(got)369 return nil370}371func jsonify(ctx ctxerr.Context, got reflect.Value) (any, *ctxerr.Error) {372 gotIf, ok := dark.GetInterface(got, true)373 if !ok {374 return nil, ctx.CannotCompareError()375 }376 b, err := ejson.Marshal(gotIf)377 if err != nil {378 if ctx.BooleanError {379 return nil, ctxerr.BooleanError380 }381 return nil, &ctxerr.Error{382 Message: "json.Marshal failed",383 Summary: ctxerr.NewSummary(err.Error()),384 }385 }386 // As Marshal succeeded, Unmarshal in an any cannot fail387 var vgot any388 ejson.Unmarshal(b, &vgot) //nolint: errcheck389 return vgot, nil390}391// summary(JSON): compares against JSON representation392// input(JSON): nil,bool,str,int,float,array,slice,map,struct,ptr393// JSON operator allows to compare the JSON representation of data394// against expectedJSON. expectedJSON can be a:395//396// - string containing JSON data like `{"fullname":"Bob","age":42}`397// - string containing a JSON filename, ending with ".json" (its398// content is [os.ReadFile] before unmarshaling)399// - []byte containing JSON data400// - [io.Reader] stream containing JSON data (is [io.ReadAll]401// before unmarshaling)402//403// expectedJSON JSON value can contain placeholders. The params404// are for any placeholder parameters in expectedJSON. params can405// contain [TestDeep] operators as well as raw values. A placeholder can406// be numeric like $2 or named like $name and always references an407// item in params.408//409// Numeric placeholders reference the n'th "operators" item (starting410// at 1). Named placeholders are used with [Tag] operator as follows:411//412// td.Cmp(t, gotValue,413// td.JSON(`{"fullname": $name, "age": $2, "gender": $3}`,414// td.Tag("name", td.HasPrefix("Foo")), // matches $1 and $name415// td.Between(41, 43), // matches only $2416// "male")) // matches only $3417//418// Note that placeholders can be double-quoted as in:419//420// td.Cmp(t, gotValue,421// td.JSON(`{"fullname": "$name", "age": "$2", "gender": "$3"}`,422// td.Tag("name", td.HasPrefix("Foo")), // matches $1 and $name423// td.Between(41, 43), // matches only $2424// "male")) // matches only $3425//426// It makes no difference whatever the underlying type of the replaced427// item is (= double quoting a placeholder matching a number is not a428// problem). It is just a matter of taste, double-quoting placeholders429// can be preferred when the JSON data has to conform to the JSON430// specification, like when used in a ".json" file.431//432// JSON does its best to convert back the JSON corresponding to a433// placeholder to the type of the placeholder or, if the placeholder434// is an operator, to the type behind the operator. Allowing to do435// things like:436//437// td.Cmp(t, gotValue, td.JSON(`{"foo":$1}`, []int{1, 2, 3, 4}))438// td.Cmp(t, gotValue,439// td.JSON(`{"foo":$1}`, []any{1, 2, td.Between(2, 4), 4}))440// td.Cmp(t, gotValue, td.JSON(`{"foo":$1}`, td.Between(27, 32)))441//442// Of course, it does this conversion only if the expected type can be443// guessed. In the case the conversion cannot occur, data is compared444// as is, in its freshly unmarshaled JSON form (so as bool, float64,445// string, []any, map[string]any or simply nil).446//447// Note expectedJSON can be a []byte, a JSON filename or a [io.Reader]:448//449// td.Cmp(t, gotValue, td.JSON("file.json", td.Between(12, 34)))450// td.Cmp(t, gotValue, td.JSON([]byte(`[1, $1, 3]`), td.Between(12, 34)))451// td.Cmp(t, gotValue, td.JSON(osFile, td.Between(12, 34)))452//453// A JSON filename ends with ".json".454//455// To avoid a legit "$" string prefix causes a bad placeholder error,456// just double it to escape it. Note it is only needed when the "$" is457// the first character of a string:458//459// td.Cmp(t, gotValue,460// td.JSON(`{"fullname": "$name", "details": "$$info", "age": $2}`,461// td.Tag("name", td.HasPrefix("Foo")), // matches $1 and $name462// td.Between(41, 43))) // matches only $2463//464// For the "details" key, the raw value "$info" is expected, no465// placeholders are involved here.466//467// Note that [Lax] mode is automatically enabled by JSON operator to468// simplify numeric tests.469//470// Comments can be embedded in JSON data:471//472// td.Cmp(t, gotValue,473// td.JSON(`474// {475// // A guy properties:476// "fullname": "$name", // The full name of the guy477// "details": "$$info", // Literally "$info", thanks to "$" escape478// "age": $2 /* The age of the guy:479// - placeholder unquoted, but could be without480// any change481// - to demonstrate a multi-lines comment */482// }`,483// td.Tag("name", td.HasPrefix("Foo")), // matches $1 and $name484// td.Between(41, 43))) // matches only $2485//486// Comments, like in go, have 2 forms. To quote the Go language specification:487// - line comments start with the character sequence // and stop at the488// end of the line.489// - multi-lines comments start with the character sequence /* and stop490// with the first subsequent character sequence */.491//492// Other JSON divergences:493// - ',' can precede a '}' or a ']' (as in go);494// - strings can contain non-escaped \n, \r and \t;495// - raw strings are accepted (r{raw}, r!raw!, â¦), see below;496// - int_lit & float_lit numbers as defined in go spec are accepted;497// - numbers can be prefixed by '+'.498//499// Most operators can be directly embedded in JSON without requiring500// any placeholder. If an operators does not take any parameter, the501// parenthesis can be omitted.502//503// td.Cmp(t, gotValue,504// td.JSON(`505// {506// "fullname": HasPrefix("Foo"),507// "age": Between(41, 43),508// "details": SuperMapOf({509// "address": NotEmpty, // () are optional when no parameters510// "car": Any("Peugeot", "Tesla", "Jeep") // any of these511// })512// }`))513//514// Placeholders can be used anywhere, even in operators parameters as in:515//516// td.Cmp(t, gotValue, td.JSON(`{"fullname": HasPrefix($1)}`, "Zip"))517//518// A few notes about operators embedding:519// - [SubMapOf] and [SuperMapOf] take only one parameter, a JSON object;520// - the optional 3rd parameter of [Between] has to be specified as a string521// and can be: "[]" or "BoundsInIn" (default), "[[" or "BoundsInOut",522// "]]" or "BoundsOutIn", "][" or "BoundsOutOut";523// - not all operators are embeddable only the following are: [All],524// [Any], [ArrayEach], [Bag], [Between], [Contains],525// [ContainsKey], [Empty], [First], [Grep], [Gt], [Gte],526// [HasPrefix], [HasSuffix], [Ignore], [JSONPointer], [Keys],527// [Last], [Len], [Lt], [Lte], [MapEach], [N], [NaN], [Nil],528// [None], [Not], [NotAny], [NotEmpty], [NotNaN], [NotNil],529// [NotZero], [Re], [ReAll], [Set], [SubBagOf], [SubMapOf],530// [SubSetOf], [SuperBagOf], [SuperMapOf], [SuperSetOf], [Values]531// and [Zero].532//533// It is also possible to embed operators in JSON strings. This way,534// the JSON specification can be fulfilled. To avoid collision with535// possible strings, just prefix the first operator name with536// "$^". The previous example becomes:537//538// td.Cmp(t, gotValue,539// td.JSON(`540// {541// "fullname": "$^HasPrefix(\"Foo\")",542// "age": "$^Between(41, 43)",543// "details": "$^SuperMapOf({544// \"address\": NotEmpty, // () are optional when no parameters545// \"car\": Any(\"Peugeot\", \"Tesla\", \"Jeep\") // any of these546// })"547// }`))548//549// As you can see, in this case, strings in strings have to be550// escaped. Fortunately, newlines are accepted, but unfortunately they551// are forbidden by JSON specification. To avoid too much escaping,552// raw strings are accepted. A raw string is a "r" followed by a553// delimiter, the corresponding delimiter closes the string. The554// following raw strings are all the same as "foo\\bar(\"zip\")!":555// - r'foo\bar"zip"!'556// - r,foo\bar"zip"!,557// - r%foo\bar"zip"!%558// - r(foo\bar("zip")!)559// - r{foo\bar("zip")!}560// - r[foo\bar("zip")!]561// - r<foo\bar("zip")!>562//563// So non-bracketing delimiters use the same character before and564// after, but the 4 sorts of ASCII brackets (round, angle, square,565// curly) all nest: r[x[y]z] equals "x[y]z". The end delimiter cannot566// be escaped.567//568// With raw strings, the previous example becomes:569//570// td.Cmp(t, gotValue,571// td.JSON(`572// {573// "fullname": "$^HasPrefix(r<Foo>)",574// "age": "$^Between(41, 43)",575// "details": "$^SuperMapOf({576// r<address>: NotEmpty, // () are optional when no parameters577// r<car>: Any(r<Peugeot>, r<Tesla>, r<Jeep>) // any of these578// })"579// }`))580//581// Note that raw strings are accepted anywhere, not only in original582// JSON strings.583//584// To be complete, $^ can prefix an operator even outside a585// string. This is accepted for compatibility purpose as the first586// operator embedding feature used this way to embed some operators.587//588// So the following calls are all equivalent:589//590// td.Cmp(t, gotValue, td.JSON(`{"id": $1}`, td.NotZero()))591// td.Cmp(t, gotValue, td.JSON(`{"id": NotZero}`))592// td.Cmp(t, gotValue, td.JSON(`{"id": NotZero()}`))593// td.Cmp(t, gotValue, td.JSON(`{"id": $^NotZero}`))594// td.Cmp(t, gotValue, td.JSON(`{"id": $^NotZero()}`))595// td.Cmp(t, gotValue, td.JSON(`{"id": "$^NotZero"}`))596// td.Cmp(t, gotValue, td.JSON(`{"id": "$^NotZero()"}`))597//598// As for placeholders, there is no differences between $^NotZero and599// "$^NotZero".600//601// TypeBehind method returns the [reflect.Type] of the expectedJSON602// once JSON unmarshaled. So it can be bool, string, float64, []any,603// map[string]any or any in case expectedJSON is "null".604//605// See also [JSONPointer], [SubJSONOf] and [SuperJSONOf].606func JSON(expectedJSON any, params ...any) TestDeep {607 j := &tdJSON{608 baseOKNil: newBaseOKNil(3),609 }610 v, err := newJSONUnmarshaler(j.GetLocation()).unmarshal(expectedJSON, params)611 if err != nil {612 j.err = err613 } else {614 j.expected = reflect.ValueOf(v)615 }616 return j617}618func (j *tdJSON) Match(ctx ctxerr.Context, got reflect.Value) *ctxerr.Error {619 if j.err != nil {620 return ctx.CollectError(j.err)621 }622 err := gotViaJSON(ctx, &got)623 if err != nil {624 return ctx.CollectError(err)625 }626 ctx.BeLax = true627 return deepValueEqual(ctx, got, j.expected)628}629func (j *tdJSON) String() string {630 if j.err != nil {631 return j.stringError()632 }633 return jsonStringify("JSON", j.expected)634}635func jsonStringify(opName string, v reflect.Value) string {636 if !v.IsValid() {637 return "JSON(null)"638 }639 var b bytes.Buffer640 b.WriteString(opName)641 b.WriteByte('(')642 json.AppendMarshal(&b, v.Interface(), len(opName)+1) //nolint: errcheck643 b.WriteByte(')')644 return b.String()645}646func (j *tdJSON) TypeBehind() reflect.Type {647 if j.err != nil {648 return nil649 }650 if j.expected.IsValid() {651 // In case we have an operator at the root, delegate it the call652 if tdOp, ok := j.expected.Interface().(TestDeep); ok {653 return tdOp.TypeBehind()654 }655 return j.expected.Type()656 }657 return types.Interface658}659type tdMapJSON struct {660 tdMap661 expected reflect.Value662}663var _ TestDeep = &tdMapJSON{}664// summary(SubJSONOf): compares struct or map against JSON665// representation but with potentially some exclusions666// input(SubJSONOf): map,struct,ptr(ptr on map/struct)667// SubJSONOf operator allows to compare the JSON representation of668// data against expectedJSON. Unlike [JSON] operator, marshaled data669// must be a JSON object/map (aka {â¦}). expectedJSON can be a:670//671// - string containing JSON data like `{"fullname":"Bob","age":42}`672// - string containing a JSON filename, ending with ".json" (its673// content is [os.ReadFile] before unmarshaling)674// - []byte containing JSON data675// - [io.Reader] stream containing JSON data (is [io.ReadAll] before676// unmarshaling)677//678// JSON data contained in expectedJSON must be a JSON object/map679// (aka {â¦}) too. During a match, each expected entry should match in680// the compared map. But some expected entries can be missing from the681// compared map.682//683// type MyStruct struct {684// Name string `json:"name"`685// Age int `json:"age"`686// }687// got := MyStruct{688// Name: "Bob",689// Age: 42,690// }691// td.Cmp(t, got, td.SubJSONOf(`{"name": "Bob", "age": 42, "city": "NY"}`)) // succeeds692// td.Cmp(t, got, td.SubJSONOf(`{"name": "Bob", "zip": 666}`)) // fails, extra "age"693//694// expectedJSON JSON value can contain placeholders. The params695// are for any placeholder parameters in expectedJSON. params can696// contain [TestDeep] operators as well as raw values. A placeholder can697// be numeric like $2 or named like $name and always references an698// item in params.699//700// Numeric placeholders reference the n'th "operators" item (starting701// at 1). Named placeholders are used with [Tag] operator as follows:702//703// td.Cmp(t, gotValue,704// td.SubJSONOf(`{"fullname": $name, "age": $2, "gender": $3}`,705// td.Tag("name", td.HasPrefix("Foo")), // matches $1 and $name706// td.Between(41, 43), // matches only $2707// "male")) // matches only $3708//709// Note that placeholders can be double-quoted as in:710//711// td.Cmp(t, gotValue,712// td.SubJSONOf(`{"fullname": "$name", "age": "$2", "gender": "$3"}`,713// td.Tag("name", td.HasPrefix("Foo")), // matches $1 and $name714// td.Between(41, 43), // matches only $2715// "male")) // matches only $3716//717// It makes no difference whatever the underlying type of the replaced718// item is (= double quoting a placeholder matching a number is not a719// problem). It is just a matter of taste, double-quoting placeholders720// can be preferred when the JSON data has to conform to the JSON721// specification, like when used in a ".json" file.722//723// SubJSONOf does its best to convert back the JSON corresponding to a724// placeholder to the type of the placeholder or, if the placeholder725// is an operator, to the type behind the operator. Allowing to do726// things like:727//728// td.Cmp(t, gotValue,729// td.SubJSONOf(`{"foo":$1, "bar": 12}`, []int{1, 2, 3, 4}))730// td.Cmp(t, gotValue,731// td.SubJSONOf(`{"foo":$1, "bar": 12}`, []any{1, 2, td.Between(2, 4), 4}))732// td.Cmp(t, gotValue,733// td.SubJSONOf(`{"foo":$1, "bar": 12}`, td.Between(27, 32)))734//735// Of course, it does this conversion only if the expected type can be736// guessed. In the case the conversion cannot occur, data is compared737// as is, in its freshly unmarshaled JSON form (so as bool, float64,738// string, []any, map[string]any or simply nil).739//740// Note expectedJSON can be a []byte, JSON filename or [io.Reader]:741//742// td.Cmp(t, gotValue, td.SubJSONOf("file.json", td.Between(12, 34)))743// td.Cmp(t, gotValue, td.SubJSONOf([]byte(`[1, $1, 3]`), td.Between(12, 34)))744// td.Cmp(t, gotValue, td.SubJSONOf(osFile, td.Between(12, 34)))745//746// A JSON filename ends with ".json".747//748// To avoid a legit "$" string prefix causes a bad placeholder error,749// just double it to escape it. Note it is only needed when the "$" is750// the first character of a string:751//752// td.Cmp(t, gotValue,753// td.SubJSONOf(`{"fullname": "$name", "details": "$$info", "age": $2}`,754// td.Tag("name", td.HasPrefix("Foo")), // matches $1 and $name755// td.Between(41, 43))) // matches only $2756//757// For the "details" key, the raw value "$info" is expected, no758// placeholders are involved here.759//760// Note that [Lax] mode is automatically enabled by SubJSONOf operator to761// simplify numeric tests.762//763// Comments can be embedded in JSON data:764//765// td.Cmp(t, gotValue,766// SubJSONOf(`767// {768// // A guy properties:769// "fullname": "$name", // The full name of the guy770// "details": "$$info", // Literally "$info", thanks to "$" escape771// "age": $2 /* The age of the guy:772// - placeholder unquoted, but could be without773// any change774// - to demonstrate a multi-lines comment */775// }`,776// td.Tag("name", td.HasPrefix("Foo")), // matches $1 and $name777// td.Between(41, 43))) // matches only $2778//779// Comments, like in go, have 2 forms. To quote the Go language specification:780// - line comments start with the character sequence // and stop at the781// end of the line.782// - multi-lines comments start with the character sequence /* and stop783// with the first subsequent character sequence */.784//785// Other JSON divergences:786// - ',' can precede a '}' or a ']' (as in go);787// - strings can contain non-escaped \n, \r and \t;788// - raw strings are accepted (r{raw}, r!raw!, â¦), see below;789// - int_lit & float_lit numbers as defined in go spec are accepted;790// - numbers can be prefixed by '+'.791//792// Most operators can be directly embedded in SubJSONOf without requiring793// any placeholder. If an operators does not take any parameter, the794// parenthesis can be omitted.795//796// td.Cmp(t, gotValue,797// td.SubJSONOf(`798// {799// "fullname": HasPrefix("Foo"),800// "age": Between(41, 43),801// "details": SuperMapOf({802// "address": NotEmpty, // () are optional when no parameters803// "car": Any("Peugeot", "Tesla", "Jeep") // any of these804// })805// }`))806//807// Placeholders can be used anywhere, even in operators parameters as in:808//809// td.Cmp(t, gotValue,810// td.SubJSONOf(`{"fullname": HasPrefix($1), "bar": 42}`, "Zip"))811//812// A few notes about operators embedding:813// - [SubMapOf] and [SuperMapOf] take only one parameter, a JSON object;814// - the optional 3rd parameter of [Between] has to be specified as a string815// and can be: "[]" or "BoundsInIn" (default), "[[" or "BoundsInOut",816// "]]" or "BoundsOutIn", "][" or "BoundsOutOut";817// - not all operators are embeddable only the following are: [All],818// [Any], [ArrayEach], [Bag], [Between], [Contains],819// [ContainsKey], [Empty], [First], [Grep], [Gt], [Gte],820// [HasPrefix], [HasSuffix], [Ignore], [JSONPointer], [Keys],821// [Last], [Len], [Lt], [Lte], [MapEach], [N], [NaN], [Nil],822// [None], [Not], [NotAny], [NotEmpty], [NotNaN], [NotNil],823// [NotZero], [Re], [ReAll], [Set], [SubBagOf], [SubMapOf],824// [SubSetOf], [SuperBagOf], [SuperMapOf], [SuperSetOf], [Values]825// and [Zero].826//827// It is also possible to embed operators in JSON strings. This way,828// the JSON specification can be fulfilled. To avoid collision with829// possible strings, just prefix the first operator name with830// "$^". The previous example becomes:831//832// td.Cmp(t, gotValue,833// td.SubJSONOf(`834// {835// "fullname": "$^HasPrefix(\"Foo\")",836// "age": "$^Between(41, 43)",837// "details": "$^SuperMapOf({838// \"address\": NotEmpty, // () are optional when no parameters839// \"car\": Any(\"Peugeot\", \"Tesla\", \"Jeep\") // any of these840// })"841// }`))842//843// As you can see, in this case, strings in strings have to be844// escaped. Fortunately, newlines are accepted, but unfortunately they845// are forbidden by JSON specification. To avoid too much escaping,846// raw strings are accepted. A raw string is a "r" followed by a847// delimiter, the corresponding delimiter closes the string. The848// following raw strings are all the same as "foo\\bar(\"zip\")!":849// - r'foo\bar"zip"!'850// - r,foo\bar"zip"!,851// - r%foo\bar"zip"!%852// - r(foo\bar("zip")!)853// - r{foo\bar("zip")!}854// - r[foo\bar("zip")!]855// - r<foo\bar("zip")!>856//857// So non-bracketing delimiters use the same character before and858// after, but the 4 sorts of ASCII brackets (round, angle, square,859// curly) all nest: r[x[y]z] equals "x[y]z". The end delimiter cannot860// be escaped.861//862// With raw strings, the previous example becomes:863//864// td.Cmp(t, gotValue,865// td.SubJSONOf(`866// {867// "fullname": "$^HasPrefix(r<Foo>)",868// "age": "$^Between(41, 43)",869// "details": "$^SuperMapOf({870// r<address>: NotEmpty, // () are optional when no parameters871// r<car>: Any(r<Peugeot>, r<Tesla>, r<Jeep>) // any of these872// })"873// }`))874//875// Note that raw strings are accepted anywhere, not only in original876// JSON strings.877//878// To be complete, $^ can prefix an operator even outside a879// string. This is accepted for compatibility purpose as the first880// operator embedding feature used this way to embed some operators.881//882// So the following calls are all equivalent:883//884// td.Cmp(t, gotValue, td.SubJSONOf(`{"id": $1}`, td.NotZero()))885// td.Cmp(t, gotValue, td.SubJSONOf(`{"id": NotZero}`))886// td.Cmp(t, gotValue, td.SubJSONOf(`{"id": NotZero()}`))887// td.Cmp(t, gotValue, td.SubJSONOf(`{"id": $^NotZero}`))888// td.Cmp(t, gotValue, td.SubJSONOf(`{"id": $^NotZero()}`))889// td.Cmp(t, gotValue, td.SubJSONOf(`{"id": "$^NotZero"}`))890// td.Cmp(t, gotValue, td.SubJSONOf(`{"id": "$^NotZero()"}`))891//892// As for placeholders, there is no differences between $^NotZero and893// "$^NotZero".894//895// TypeBehind method returns the map[string]any type.896//897// See also [JSON], [JSONPointer] and [SuperJSONOf].898func SubJSONOf(expectedJSON any, params ...any) TestDeep {899 m := &tdMapJSON{900 tdMap: tdMap{901 tdExpectedType: tdExpectedType{902 base: newBase(3),903 expectedType: reflect.TypeOf((map[string]any)(nil)),904 },905 kind: subMap,906 },907 }908 v, err := newJSONUnmarshaler(m.GetLocation()).unmarshal(expectedJSON, params)909 if err != nil {910 m.err = err911 return m912 }913 _, ok := v.(map[string]any)914 if !ok {915 m.err = ctxerr.OpBad("SubJSONOf", "SubJSONOf() only accepts JSON objects {â¦}")916 return m917 }918 m.expected = reflect.ValueOf(v)919 m.populateExpectedEntries(nil, m.expected)920 return m921}922// summary(SuperJSONOf): compares struct or map against JSON923// representation but with potentially extra entries924// input(SuperJSONOf): map,struct,ptr(ptr on map/struct)925// SuperJSONOf operator allows to compare the JSON representation of926// data against expectedJSON. Unlike JSON operator, marshaled data927// must be a JSON object/map (aka {â¦}). expectedJSON can be a:928//929// - string containing JSON data like `{"fullname":"Bob","age":42}`930// - string containing a JSON filename, ending with ".json" (its931// content is [os.ReadFile] before unmarshaling)932// - []byte containing JSON data933// - [io.Reader] stream containing JSON data (is [io.ReadAll] before934// unmarshaling)935//936// JSON data contained in expectedJSON must be a JSON object/map937// (aka {â¦}) too. During a match, each expected entry should match in938// the compared map. But some entries in the compared map may not be939// expected.940//941// type MyStruct struct {942// Name string `json:"name"`943// Age int `json:"age"`944// City string `json:"city"`945// }946// got := MyStruct{947// Name: "Bob",948// Age: 42,949// City: "TestCity",950// }951// td.Cmp(t, got, td.SuperJSONOf(`{"name": "Bob", "age": 42}`)) // succeeds952// td.Cmp(t, got, td.SuperJSONOf(`{"name": "Bob", "zip": 666}`)) // fails, miss "zip"953//954// expectedJSON JSON value can contain placeholders. The params are955// for any placeholder parameters in expectedJSON. params can contain956// [TestDeep] operators as well as raw values. A placeholder can be957// numeric like $2 or named like $name and always references an item958// in params.959//960// Numeric placeholders reference the n'th "operators" item (starting961// at 1). Named placeholders are used with [Tag] operator as follows:962//963// td.Cmp(t, gotValue,964// SuperJSONOf(`{"fullname": $name, "age": $2, "gender": $3}`,965// td.Tag("name", td.HasPrefix("Foo")), // matches $1 and $name966// td.Between(41, 43), // matches only $2967// "male")) // matches only $3968//969// Note that placeholders can be double-quoted as in:970//971// td.Cmp(t, gotValue,972// td.SuperJSONOf(`{"fullname": "$name", "age": "$2", "gender": "$3"}`,973// td.Tag("name", td.HasPrefix("Foo")), // matches $1 and $name974// td.Between(41, 43), // matches only $2975// "male")) // matches only $3976//977// It makes no difference whatever the underlying type of the replaced978// item is (= double quoting a placeholder matching a number is not a979// problem). It is just a matter of taste, double-quoting placeholders980// can be preferred when the JSON data has to conform to the JSON981// specification, like when used in a ".json" file.982//983// SuperJSONOf does its best to convert back the JSON corresponding to a984// placeholder to the type of the placeholder or, if the placeholder985// is an operator, to the type behind the operator. Allowing to do986// things like:987//988// td.Cmp(t, gotValue,989// td.SuperJSONOf(`{"foo":$1}`, []int{1, 2, 3, 4}))990// td.Cmp(t, gotValue,991// td.SuperJSONOf(`{"foo":$1}`, []any{1, 2, td.Between(2, 4), 4}))992// td.Cmp(t, gotValue,993// td.SuperJSONOf(`{"foo":$1}`, td.Between(27, 32)))994//995// Of course, it does this conversion only if the expected type can be996// guessed. In the case the conversion cannot occur, data is compared997// as is, in its freshly unmarshaled JSON form (so as bool, float64,998// string, []any, map[string]any or simply nil).999//1000// Note expectedJSON can be a []byte, JSON filename or [io.Reader]:1001//1002// td.Cmp(t, gotValue, td.SuperJSONOf("file.json", td.Between(12, 34)))1003// td.Cmp(t, gotValue, td.SuperJSONOf([]byte(`[1, $1, 3]`), td.Between(12, 34)))1004// td.Cmp(t, gotValue, td.SuperJSONOf(osFile, td.Between(12, 34)))1005//1006// A JSON filename ends with ".json".1007//1008// To avoid a legit "$" string prefix causes a bad placeholder error,1009// just double it to escape it. Note it is only needed when the "$" is1010// the first character of a string:1011//1012// td.Cmp(t, gotValue,1013// td.SuperJSONOf(`{"fullname": "$name", "details": "$$info", "age": $2}`,1014// td.Tag("name", td.HasPrefix("Foo")), // matches $1 and $name1015// td.Between(41, 43))) // matches only $21016//1017// For the "details" key, the raw value "$info" is expected, no1018// placeholders are involved here.1019//1020// Note that [Lax] mode is automatically enabled by SuperJSONOf operator to1021// simplify numeric tests.1022//1023// Comments can be embedded in JSON data:1024//1025// td.Cmp(t, gotValue,1026// td.SuperJSONOf(`1027// {1028// // A guy properties:1029// "fullname": "$name", // The full name of the guy1030// "details": "$$info", // Literally "$info", thanks to "$" escape1031// "age": $2 /* The age of the guy:1032// - placeholder unquoted, but could be without1033// any change1034// - to demonstrate a multi-lines comment */1035// }`,1036// td.Tag("name", td.HasPrefix("Foo")), // matches $1 and $name1037// td.Between(41, 43))) // matches only $21038//1039// Comments, like in go, have 2 forms. To quote the Go language specification:1040// - line comments start with the character sequence // and stop at the1041// end of the line.1042// - multi-lines comments start with the character sequence /* and stop1043// with the first subsequent character sequence */.1044//1045// Other JSON divergences:1046// - ',' can precede a '}' or a ']' (as in go);1047// - strings can contain non-escaped \n, \r and \t;1048// - raw strings are accepted (r{raw}, r!raw!, â¦), see below;1049// - int_lit & float_lit numbers as defined in go spec are accepted;1050// - numbers can be prefixed by '+'.1051//1052// Most operators can be directly embedded in SuperJSONOf without requiring1053// any placeholder. If an operators does not take any parameter, the1054// parenthesis can be omitted.1055//1056// td.Cmp(t, gotValue,1057// td.SuperJSONOf(`1058// {1059// "fullname": HasPrefix("Foo"),1060// "age": Between(41, 43),1061// "details": SuperMapOf({1062// "address": NotEmpty, // () are optional when no parameters1063// "car": Any("Peugeot", "Tesla", "Jeep") // any of these1064// })1065// }`))1066//1067// Placeholders can be used anywhere, even in operators parameters as in:1068//1069// td.Cmp(t, gotValue, td.SuperJSONOf(`{"fullname": HasPrefix($1)}`, "Zip"))1070//1071// A few notes about operators embedding:1072// - [SubMapOf] and [SuperMapOf] take only one parameter, a JSON object;1073// - the optional 3rd parameter of [Between] has to be specified as a string1074// and can be: "[]" or "BoundsInIn" (default), "[[" or "BoundsInOut",1075// "]]" or "BoundsOutIn", "][" or "BoundsOutOut";1076// - not all operators are embeddable only the following are: [All],1077// [Any], [ArrayEach], [Bag], [Between], [Contains],1078// [ContainsKey], [Empty], [First], [Grep], [Gt], [Gte],1079// [HasPrefix], [HasSuffix], [Ignore], [JSONPointer], [Keys],1080// [Last], [Len], [Lt], [Lte], [MapEach], [N], [NaN], [Nil],1081// [None], [Not], [NotAny], [NotEmpty], [NotNaN], [NotNil],1082// [NotZero], [Re], [ReAll], [Set], [SubBagOf], [SubMapOf],1083// [SubSetOf], [SuperBagOf], [SuperMapOf], [SuperSetOf], [Values]1084// and [Zero].1085//1086// It is also possible to embed operators in JSON strings. This way,1087// the JSON specification can be fulfilled. To avoid collision with1088// possible strings, just prefix the first operator name with1089// "$^". The previous example becomes:1090//1091// td.Cmp(t, gotValue,1092// td.SuperJSONOf(`1093// {1094// "fullname": "$^HasPrefix(\"Foo\")",1095// "age": "$^Between(41, 43)",1096// "details": "$^SuperMapOf({1097// \"address\": NotEmpty, // () are optional when no parameters1098// \"car\": Any(\"Peugeot\", \"Tesla\", \"Jeep\") // any of these1099// })"1100// }`))1101//1102// As you can see, in this case, strings in strings have to be1103// escaped. Fortunately, newlines are accepted, but unfortunately they1104// are forbidden by JSON specification. To avoid too much escaping,1105// raw strings are accepted. A raw string is a "r" followed by a1106// delimiter, the corresponding delimiter closes the string. The1107// following raw strings are all the same as "foo\\bar(\"zip\")!":1108// - r'foo\bar"zip"!'1109// - r,foo\bar"zip"!,1110// - r%foo\bar"zip"!%1111// - r(foo\bar("zip")!)1112// - r{foo\bar("zip")!}1113// - r[foo\bar("zip")!]1114// - r<foo\bar("zip")!>1115//1116// So non-bracketing delimiters use the same character before and1117// after, but the 4 sorts of ASCII brackets (round, angle, square,1118// curly) all nest: r[x[y]z] equals "x[y]z". The end delimiter cannot1119// be escaped.1120//1121// With raw strings, the previous example becomes:1122//1123// td.Cmp(t, gotValue,1124// td.SuperJSONOf(`1125// {1126// "fullname": "$^HasPrefix(r<Foo>)",1127// "age": "$^Between(41, 43)",1128// "details": "$^SuperMapOf({1129// r<address>: NotEmpty, // () are optional when no parameters1130// r<car>: Any(r<Peugeot>, r<Tesla>, r<Jeep>) // any of these1131// })"1132// }`))1133//1134// Note that raw strings are accepted anywhere, not only in original1135// JSON strings.1136//1137// To be complete, $^ can prefix an operator even outside a1138// string. This is accepted for compatibility purpose as the first1139// operator embedding feature used this way to embed some operators.1140//1141// So the following calls are all equivalent:1142//1143// td.Cmp(t, gotValue, td.SuperJSONOf(`{"id": $1}`, td.NotZero()))1144// td.Cmp(t, gotValue, td.SuperJSONOf(`{"id": NotZero}`))1145// td.Cmp(t, gotValue, td.SuperJSONOf(`{"id": NotZero()}`))1146// td.Cmp(t, gotValue, td.SuperJSONOf(`{"id": $^NotZero}`))1147// td.Cmp(t, gotValue, td.SuperJSONOf(`{"id": $^NotZero()}`))1148// td.Cmp(t, gotValue, td.SuperJSONOf(`{"id": "$^NotZero"}`))1149// td.Cmp(t, gotValue, td.SuperJSONOf(`{"id": "$^NotZero()"}`))1150//1151// As for placeholders, there is no differences between $^NotZero and1152// "$^NotZero".1153//1154// TypeBehind method returns the map[string]any type.1155//1156// See also [JSON], [JSONPointer] and [SubJSONOf].1157func SuperJSONOf(expectedJSON any, params ...any) TestDeep {1158 m := &tdMapJSON{1159 tdMap: tdMap{1160 tdExpectedType: tdExpectedType{1161 base: newBase(3),1162 expectedType: reflect.TypeOf((map[string]any)(nil)),1163 },1164 kind: superMap,1165 },1166 }1167 v, err := newJSONUnmarshaler(m.GetLocation()).unmarshal(expectedJSON, params)1168 if err != nil {1169 m.err = err1170 return m1171 }1172 _, ok := v.(map[string]any)1173 if !ok {1174 m.err = ctxerr.OpBad("SuperJSONOf", "SuperJSONOf() only accepts JSON objects {â¦}")1175 return m1176 }1177 m.expected = reflect.ValueOf(v)1178 m.populateExpectedEntries(nil, m.expected)1179 return m1180}1181func (m *tdMapJSON) Match(ctx ctxerr.Context, got reflect.Value) *ctxerr.Error {1182 if m.err != nil {1183 return ctx.CollectError(m.err)1184 }1185 err := gotViaJSON(ctx, &got)1186 if err != nil {1187 return ctx.CollectError(err)1188 }1189 // nil case1190 if !got.IsValid() {1191 if ctx.BooleanError {1192 return ctxerr.BooleanError1193 }1194 return ctx.CollectError(&ctxerr.Error{1195 Message: "values differ",1196 Got: types.RawString("null"),1197 Expected: types.RawString("non-null"),1198 })1199 }...
gotViaJSON
Using AI Code Generation
1import (2type td struct {3}4func (t *td) gotViaJSON() {5 if err != nil {6 fmt.Println("Error in getting response")7 }8 defer resp.Body.Close()9 body, err := ioutil.ReadAll(resp.Body)10 if err != nil {11 fmt.Println("Error in reading response")12 }13 if err := json.Unmarshal(body, t); err != nil {14 fmt.Println("Error in unmarshalling")15 }16}17func main() {18 t.gotViaJSON()19 fmt.Println(t)20}21{John 32}22import (23type td struct {24}25func main() {26 data := []byte(`{"name":"John","age":32}`)27 err := json.Unmarshal(data, &t)28 if err != nil {29 fmt.Println("Error in unmarshalling")30 }31 fmt.Println(t)32}33{John 32}34import (35type td struct {36}37func main() {38 t := td{
gotViaJSON
Using AI Code Generation
1import (2func main() {3 fmt.Println("Hello, playground")4 td := new(TestData)5 td.gotViaJSON()6}7import (8func main() {9 fmt.Println("Hello, playground")10 td := TestData{}11 td.gotViaJSON()12}13import (14func main() {15 fmt.Println("Hello, playground")16 td := TestData{}17 td.gotViaJSON()18}19import (20func main() {21 fmt.Println("Hello, playground")22 td := TestData{}23 td.gotViaJSON()24}25import (26func main() {27 fmt.Println("Hello, playground")28 td := TestData{}29 td.gotViaJSON()30}31import (32func main() {33 fmt.Println("Hello, playground")34 td := TestData{}35 td.gotViaJSON()36}37import (38func main() {39 fmt.Println("Hello, playground")40 td := TestData{}41 td.gotViaJSON()42}43import (44func main() {45 fmt.Println("Hello, playground")46 td := TestData{}47 td.gotViaJSON()48}49import (50func main() {51 fmt.Println("Hello, playground")52 td := TestData{}53 td.gotViaJSON()54}55import (
gotViaJSON
Using AI Code Generation
1import "fmt"2import "encoding/json"3type td struct {4}5func main() {6 fmt.Println("Hello, playground")7 t := td{"John", 23}8 b, _ := json.Marshal(t)9 fmt.Println(string(b))10 t2 := td{}11 json.Unmarshal(b, &t2)12 fmt.Println(t2)13}14import "fmt"15import "encoding/json"16type td struct {17}18func main() {19 fmt.Println("Hello, playground")20 t := td{"John", 23}21 b, _ := json.Marshal(t)22 fmt.Println(string(b))23 t2 := td{}24 json.Unmarshal(b, &t2)25 fmt.Println(t2)26}27import "fmt"28import "encoding/json"29type td struct {30}31func main() {32 fmt.Println("Hello, playground")33 t := td{"John", 23}34 b, _ := json.Marshal(t)35 fmt.Println(string(b))36 t2 := td{}37 json.Unmarshal(b, &t2)38 fmt.Println(t2)39}40import "fmt"41import "encoding/json"42type td struct {43}44func main() {45 fmt.Println("Hello, playground")46 t := td{"John", 23}47 b, _ := json.Marshal(t)48 fmt.Println(string(b))49 t2 := td{}50 json.Unmarshal(b, &t2)51 fmt.Println(t2)52}53import "fmt"54import "encoding/json"55type td struct {56}57func main() {58 fmt.Println("Hello, playground")59 t := td{"John", 23
gotViaJSON
Using AI Code Generation
1import (2func main() {3 td := new(td)4 td.gotViaJSON()5 fmt.Println(td.Result)6}7import (8func main() {9 td := new(td)10 td.gotViaJSON()11 fmt.Println(td.Result)12}13import (14func main() {15 td := new(td)16 td.gotViaJSON()17 fmt.Println(td.Result)18}19import (20func main() {21 td := new(td)22 td.gotViaJSON()23 fmt.Println(td.Result)24}25import (26func main() {27 td := new(td)28 td.gotViaJSON()29 fmt.Println(td.Result)30}31import (32func main() {33 td := new(td)34 td.gotViaJSON()35 fmt.Println(td.Result)36}37import (38func main() {39 td := new(td)
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!