json

#Reading

#Tooling

#Libraries

#Recipes

#ordered json

See next example of inlining, with help of sorted slics we can have sorted keys in json.

//  - unordered.go - 
// Working with unordered maps json data
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"sort"
)

type pkgjs struct {
	Order []string
	Map   map[string]interface{}
}

func main() {
	jsonStr := `
	{
		"name": "foo",
		"dependencies": {
			"@serverless/cli": "^1.5.2",
			"yaml-ast-parser": "0.0.43"
		},
		"foobar": "barbar"
	}`

	var s pkgjs

	if err := json.Unmarshal([]byte(jsonStr), &s); err != nil {
		log.Fatal(err)
	}

	// adding new values
	key, value := "sss", struct {
		Data struct {
			Url     string `json:"url,omitempty"`
			Version string `json:"version,omitempty"`
			GitHash string `json:"git_commit,omitempty"`
		} `json:"data"`
	}{}

	value.Data.Url = "http://example.com"

	s.Replace(key, value)
	b1, _ := s.MarshalJSON()
	fmt.Printf("Result:\n %s\n", b1)

	b2, _ := json.Marshal(s)
	fmt.Printf("Result:\n %s\n", b2)
}

func (pj *pkgjs) UnmarshalJSON(b []byte) error {
	if err := json.Unmarshal(b, &pj.Map); err != nil {
		return err
	}

	//nolint: wsl
	index := make(map[string]int)
	for key := range pj.Map {
		pj.Order = append(pj.Order, key)
		esc, _ := json.Marshal(key)
		index[key] = bytes.Index(b, esc)
	}

	sort.Slice(pj.Order, func(i, j int) bool {
		return index[pj.Order[i]] < index[pj.Order[j]]
	})

	return nil
}

func (pj pkgjs) Replace(key string, value interface{}) {
	pj.Map[key] = value
}

func (pj pkgjs) MarshalJSON() ([]byte, error) {
	var buf bytes.Buffer

	fmt.Fprintf(&buf, "{\n")

	seen := map[string]struct{}{}

	// extra keys
	for i := range pj.Order {
		seen[pj.Order[i]] = struct{}{}
	}

	for k := range pj.Map {
		if _, ok := seen[k]; ok {
			continue
		}
		pj.Order = append(pj.Order, k)
	}

	// building json
	for i, key := range pj.Order {
		seen[key] = struct{}{}

		// keys
		km, err := json.Marshal(key)
		if err != nil {
			return nil, err
		}

		fmt.Fprintf(&buf, "%s%s:", "  ", km)

		// values
		vm, err := json.MarshalIndent(pj.Map[key], "  ", "    ")
		if err != nil {
			return nil, err
		}

		buf.Write(vm)

		if i != len(pj.Map)-1 {
			fmt.Fprintf(&buf, ",")
		}
		fmt.Fprintf(&buf, "\n")
	}

	// extra added
	fmt.Fprintf(&buf, "}\n")

	return buf.Bytes(), nil
}

#,inline

//  - skip_json_fileds.go - 
// -------------------------------------
// Skip fields in json (tags)
// -------------------------------------
package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	raw := []byte(`{"uno":"one", "ein":"one"}`)

	var data struct {
		Uno string `json:"uno"`
		Ein string `json:"-"` // <- this is minus, and it used for skipping.....
	}

	_ = json.Unmarshal(raw, &data)

	fmt.Printf("Unmarshaled - %#v\n", data)

	data.Ein = "odyn"

	fmt.Printf("Ein is fixed - %#v\n", data)

	jsonStr, _ := json.Marshal(data)
	fmt.Println("Marshaled", string(jsonStr))
}

#,omitempty

//  - omitempty.go - 
package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	Struct := struct {
		A string
		B string  `json:"b"`
		C string  `json:"e"`
		D string  `json:",omitempty"`
		F *string `json:",omitempty"`
		i string  `json:"r"`
	}{
		i: "i",
	}

	b, _ := json.Marshal(&Struct)
	fmt.Println(string(b))
}
>> {"A":"","b":"","e":""}

#-

json:"-" Helps to skip Records population

//  - skip_json_fileds.go - 
// -------------------------------------
// Skip fields in json (tags)
// -------------------------------------
package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	raw := []byte(`{"uno":"one", "ein":"one"}`)

	var data struct {
		Uno string `json:"uno"`
		Ein string `json:"-"` // <- this is minus, and it used for skipping.....
	}

	_ = json.Unmarshal(raw, &data)

	fmt.Printf("Unmarshaled - %#v\n", data)

	data.Ein = "odyn"

	fmt.Printf("Ein is fixed - %#v\n", data)

	jsonStr, _ := json.Marshal(data)
	fmt.Println("Marshaled", string(jsonStr))
}

#htmlquoting

//  - htmlquoting.go - 
// Dealing with html quoting
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"strings"
)

func main() {
	jsonStr := `
	{
		"name": "foo",
		"dependencies": {
			"@serverless/cli": "^1.5.2",
			"yaml-ast-parser": "0.0.43"
		},
		"foobar": "barbar"
	}`

	tmp := map[string]any{}

	if err := json.Unmarshal([]byte(jsonStr), &tmp); err != nil {
		log.Fatal(err)
	}

	body, _ := json.MarshalIndent(tmp, "  ", "  ")

	// character to rune transition
	// https://golang.org/src/encoding/json/encode.go?s=6551:6569#L188
	bodyStr := string(body)
	bodyStr = strings.ReplaceAll(bodyStr, `\u003e`, ">")
	bodyStr = strings.ReplaceAll(bodyStr, `\u003c`, "<")
	bodyStr = strings.ReplaceAll(bodyStr, `\u0026`, "&")

	fmt.Printf("%s", bodyStr)
}

#json.Raw

//  - raw.go - 
package main

import (
	"encoding/json"
	"fmt"
	"log"
)

func main() {
	jsonStr := `
	{
		"name": "foo",
		"dependencies": {
			"@serverless/cli": "^1.5.2",
			"yaml-ast-parser": "0.0.43"
		},
		"foobar": "barbar"
	}
	`
	tmp := map[string]interface{}{}

	if err := json.Unmarshal([]byte(jsonStr), &tmp); err != nil {
		log.Fatal(err)
	}

	// hiding all fields in other field!
	var out struct {
		Name string          `json:"name"`
		Raw  json.RawMessage `json:"data,omitempty"`
	}

	out.Name = tmp["name"].(string)
	out.Raw = json.RawMessage(jsonStr)

	body, _ := json.MarshalIndent(out, "  ", "  ")

	fmt.Printf("%s", string(body))
}