Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang modify json without struct

Tags:

json

go

type Struct struct {
   Value  string `json:"value"`
   Value1 string `json:"value_one"`
   Nest   Nested `json:"nest"`
}

type Nested struct {
   Something string `json:"something"`
}

I want to add elements which are not in the structs definitions without creating another struct type. For example

Struct.Extra1 = Nested{"yy"}
Struct.Nested.Extra2 = "zz"

Which will result

{
    "Value": "xx",
    "Value1": "xx",
    "Extra1": {
      "Something", "yy"
    },
    "Nest": {
      "Something": "xx",
      "Extra2": "zz"
    }
}

SOLUTION1: I thought of adding omitempty to achieve this but it makes the structs complex.

type Struct struct {
   Value  string
   Value1 string
   Nest   Nested
   Extra1 Nested `json:"omitempty"`
}

type Nested struct {
   Something string
   Extra2 string `json:"omitempty"`
}

SOLUTION2:

myextras := make(map[string]interface{})
// get Struct.Nested in map[string]interface{} format
myextras = Struct.Nest
myextras["Extra2"] = "zz"

// get Struct in map[string]interface{} format
struct["Nest"] = myextras
struct["Extra1"] = Nested{"yy"}

// solves the problem with lots of type casting but doesn't support json tag naming

Is there a better solution to add nested elements which are not represented in struct datatype with json-tag support and could be used to output to user.

like image 854
Thellimist Avatar asked Jul 17 '15 01:07

Thellimist


People also ask

What is JSON Unmarshalling?

To unmarshal a JSON array into a slice, Unmarshal resets the slice length to zero and then appends each element to the slice. As a special case, to unmarshal an empty JSON array into a slice, Unmarshal replaces the slice with a new empty slice.

How does JSON Unmarshal work in Golang?

Marshal function, will take JSON data and translate it back into Go data. You provide json. Unmarshal with the JSON data as well as the Go variable to put the unmarshalled data into and it will either return an error value if it's unable to do it, or a nil error value if it succeeded.

What is Unmarshalling in Golang?

Golang Unmarshal Unmarshal is the contrary of marshal. It allows you to convert byte data into the original data structure. In go, unmarshaling is handled by the json. Unmarshal() method.

How do I write a JSON file in Golang?

Reading and Writing JSON Files in Go It is actually pretty simple to read and write data to and from JSON files using the Go standard library. For writing struct types into a JSON file we have used the WriteFile function from the io/ioutil package. The data content is marshalled/encoded into JSON format.


3 Answers

If someone is not happy with the solution provided:

Try tidwall/sjson. It provides functions for quick JSON editing without having to define any structure. It saved me a bunch of time yesterday :D

Example usage:

value, _ := sjson.Set(`{"name":{"last":"Anderson"}}`, "name.last", "Smith")
println(value)

// Output:
// {"name":{"last":"Smith"}}
like image 85
Dhruv Batheja Avatar answered Oct 20 '22 00:10

Dhruv Batheja


Based on this answer: Can I use MarshalJSON to add arbitrary fields to a json encoding in golang?

You could do something like (demo: http://play.golang.org/p/dDiTwxhoNn):

package main

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

type Book struct {
    Title  string
    Author string

    // extra is used for additional dynamic element marshalling
    extra func() interface{}
}

type FakeBook Book

func (b *Book) SetExtra(fn func() interface{}) {
    b.extra = fn
}

func (b *Book) MarshalJSON() ([]byte, error) {
    if b.extra == nil {
        b.extra = func() interface{} { return *b }
    }

    return json.Marshal(b.extra())
}

func main() {
    ms := &Book{
        Title:  "Catch-22",
        Author: "Joseph Heller",
    }

    ms.SetExtra(func() interface{} {
        return struct {
            FakeBook
            Extra1 struct {
                Something string `json:"something"`
            } `json:"extra1"`
        }{
            FakeBook: FakeBook(*ms),
            Extra1: struct {
                Something string `json:"something"`
            }{
                Something: "yy",
            },
        }
    })

    out, err := json.MarshalIndent(ms, "", "  ")
    if err != nil {
        log.Fatalln(err)
    }
    fmt.Println(string(out))

    mb := &Book{
        Title:  "Vim-go",
        Author: "Fatih Arslan",
    }

    mb.SetExtra(func() interface{} {
        return struct {
            FakeBook
            Something string `json:"something"`
        }{
            FakeBook:  FakeBook(*mb),
            Something: "xx",
        }
    })

    out, err = json.MarshalIndent(mb, "", "  ")
    if err != nil {
        log.Fatalln(err)
    }
    fmt.Println(string(out))

    mc := &Book{
        Title:  "Another-Title",
        Author: "Fatih Arslan",
    }

    out, err = json.MarshalIndent(mc, "", "  ")
    if err != nil {
        log.Fatalln(err)
    }
    fmt.Println(string(out))
}
like image 10
Fatih Arslan Avatar answered Oct 19 '22 23:10

Fatih Arslan


yes. there is a type json.Raw which not a struct but []byte. you can manage it out of struct, in any marshal/unmarshal way.

UPDATE

use any way to edit json string on the fly

package main

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

type Struct struct {
   Value  string `json:"value"`
   Value1 string `json:"value_one"`
   Nest   json.RawMessage`json:"nest"`
}

func main() {
    s := Struct{Value1: "struct string"}
    buf, _ := json.Marshal(s)
    fmt.Println(string(buf))
    s2 := strings.ReplaceAll(string(buf), "null", `{"extra2":{"anykey":3.1415926535}}`)
    fmt.Println(s2)
}

https://play.golang.org/p/lOCxJBs5iRJ

like image 3
Jiang YD Avatar answered Oct 20 '22 01:10

Jiang YD