1

I have json data like this:

json: {"opt1":200,"opt3":"1","opt4":"13","opt5":null,"products":[{"product_id":1,"price":100,"variant_id":100},{"product_id":1,"price":100,"variant_id":null}]}

I have structured it using

type Products struct {
    Product_id int
    Price json.Number
    Variant_id int
}


type Pdata struct {
    Products []Products `json:"products"`
}

Then I use unmarshal

jsonb := []byte(jsonVal)
    var data Pdata
    err := json.Unmarshal(jsonb, &data)
    if err != nil {
        fmt.Println(err)
        return
    }

And get output like

{[{1 100 100} {2 100 0}]}

Now I need to convert that data into a json object like this

{"purchased_products": [{"product_id": 1,"price": 1200,"variation_id": 100},{"product_id": 2,"price": 100,"variation_id": null}]}

After that, I need to assign it to "json"

var d = map[string]string{
    "json":        jsonVal,
    "created_at":  time.Now().Format("2006-01-02 15:04:05"),
    "updated_at":  time.Now().Format("2006-01-02 15:04:05"),
}

How can I do it?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Animesh
  • 417
  • 2
  • 5
  • 14
  • 1
    Maybe you can take a look at this example, see if it helps. https://play.golang.org/p/9MX-hOHkOno – junwen-k Jul 02 '21 at 06:03
  • @Animesh that playground doesn't account for the `"variant_id":null` in your JSON output. Not sure if that's actually a requirement or not. Please see mine or mkopriva's answers – blackgreen Jul 02 '21 at 06:41
  • @blackgreen my requirement is ok with that output. – Animesh Jul 02 '21 at 06:52
  • @Animesh then you should clarify that in your question because valid answers are different based on that single detail – blackgreen Jul 02 '21 at 06:53

5 Answers5

2

Create a type (eg : PurchasedProducts) as below.

type PurchasedProducts struct {
    Products []Products `json:"purchased_products"`
}

And init a PurchasedProducts type variable and assign your unmarshaled products to Purchased products as below.

    pProducts := PurchasedProducts{Products: data.Products}
    jsonByte, err := json.Marshal(pProducts)
    if err != nil {
        fmt.Println(err)
        return
    }

And convert that []byte array to a string and assign it to the map like below.

    var d = map[string]string{
        "json":        string(jsonByte),
        "created_at":  time.Now().Format("2006-01-02 15:04:05"),
        "updated_at":  time.Now().Format("2006-01-02 15:04:05"),
    }

You can run and see full code here.

nipuna
  • 3,697
  • 11
  • 24
1

For nullable fields you can use pointers, so for example if the variant_id json field can be an integer or a json null, and you want to retain that information, then you can change Variant_id int to Variant_id *int.

type Product struct {
    Product_id int         `json:"product_id"`
    Price      json.Number `json:"price"`
    Variant_id *int        `json:"variant_id"`
}

To change json field names between unmarshal and marshal you can declare a second Products struct with the same fields as the original but with struct tags defining the desired field names, then, if the structs are, in all other respects, equivalent you can convert between them.

type Product struct {
    Product_id int         `json:"product_id"`
    Price      json.Number `json:"price"`
    Variant_id int         `json:"variant_id"`
}

type PurchasedProduct struct {
    Product_id int         `json:"product_id"`
    Price      json.Number `json:"price"`
    Variant_id int         `json:"variation_id"` // here variant_id becomes variation_id
}

Then, if p is of type Product, you can simply convert it to PurchasedProduct like so:

pp := PurchasedProduct(p)

To offload the conversion to the marshaling process you can have the original types implement the json.Marshaler interface and do the conversion there.

func (p Product) MarshalJSON() ([]byte, error) {
    type P struct {
        Product_id int         `json:"product_id"`
        Price      json.Number `json:"price"`
        Variant_id *int        `json:"variation_id"`
    }
    return json.Marshal(P(p))
}

With the above you can do the following:

func main() {
    // unmarshal
    var pd Pdata
    err := json.Unmarshal(data, &pd)
    if err != nil {
        panic(err)
    }

    // marshal
    out, err := json.MarshalIndent(pd, "", "  ")
    if err != nil {
        panic(err)
    }
    fmt.Println(string(out))
}

https://play.golang.org/p/0gnrjgUslza

mkopriva
  • 35,176
  • 4
  • 57
  • 71
1

Simply define two more structs that model the second JSON object:

type Pdata2 struct {
    PurchasedProducts []Product2
}

type Product2 struct {
    Product_id    int
    Price         json.Number
    Variation_id  *int // pointer to int
}

The Variation_id field is an *int type because your required output JSON shows "variation_id": null. If you declare the field as simple int, its zero value will be marshaled to 0.

Then initialize those structs using values from the previous ones:

func main() {
    data2 := Pdata2{
        PurchasedProducts: make([]Product2, len(data.Products)),
    }

    for i, p := range data.Products {
        data2.PurchasedProducts[i] = Product2{
            Product_id:   p.Product_id,
            Price:        p.Price,
            Variation_id: nullableInt(p.Variant_id),
        }
    }

    b, err := json.Marshal(data2)
    if err != nil {
        // ... handle error
    }
    var d = map[string]string{
        "json": string(b),
        // ...
    }
    fmt.Println(d)
}

func nullableInt(n int) *int {
    if n == 0 {
        return nil
    }
    return &n
}

Playground: https://play.golang.org/p/xhsmHNBjRKN

blackgreen
  • 34,072
  • 23
  • 111
  • 129
0

Here you are. Assumptions are that:

  • Product is a model which could be much more complicated, thus it has dedicated structs. Thus transformation from Product to OutputProduct can be unit tested separately.
  • It is one time use application, not a part of the application which exposes an API. Otherwise it should be properly separated into layers and the output should be written as a structure.
package main

import (
    "encoding/json"
    "log"
    "os"
    "time"
)

type (
    Product struct {
        ProductID int `json:"product_id"`
        VariantID int `json:"variant_id"`
        Price     json.Number
    }
    Products []Product

    OutputProduct struct {
        ProductID int `json:"product_id"`
        VariantID int `json:"variation_id"`
        Price     json.Number
    }
)

func (p Product) ToOutputProduct() OutputProduct {
    return OutputProduct{
        ProductID: p.ProductID,
        VariantID: p.VariantID,
        Price:     p.Price,
    }
}

func (p Products) ToOutputProducts() []OutputProduct {
    outputProducts := make([]OutputProduct, len(p))
    for i := 0; i < len(p); i++ {
        outputProducts[i] = p[i].ToOutputProduct()
    }
    return outputProducts
}

func main() {
    var inputJSON = `{"opt1":200,"opt3":"1","opt4":"13","opt5":null,"products":[{"product_id":1,"price":100,"variant_id":100},{"product_id":1,"price":100,"variant_id":null}]}`

    var parsedInput struct {
        Products Products
    }
    if err := json.Unmarshal([]byte(inputJSON), &parsedInput); err != nil {
        log.Fatal(err)
    }

    var output = map[string]interface{}{
        "json":       map[string][]OutputProduct{
            "purchased_products": parsedInput.Products.ToOutputProducts(),
        },
        "created_at": time.Now().Format("2006-01-02 15:04:05"),
        "updated_at": time.Now().Format("2006-01-02 15:04:05"),
    }
    encoder := json.NewEncoder(os.Stdout)
    encoder.SetIndent(" ", "  ")
    if err := encoder.Encode(output); err != nil {
        log.Fatal(err)
    }
}
Jaroslaw
  • 636
  • 3
  • 8
0

Based on the suggestions from comments:

package main

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

type Products struct {
    Product_id int `json:"product_id"`
    Price      int `json:"price"`
    Variant_id int `json:"variant_id"`
}

type ProductData struct {
    Products []Products `json:"products"`
}

type Response struct {
    Json      json.RawMessage `json:"json"`
    CreatedAt string          `json:"created_at"`
    UpdatedAt string          `json:"updated_at"`
}

func main() {
    productJson := `{
        "opt1": 200,
        "opt3": "1",
        "opt4": "13",
        "opt5": null,
        "products": [
            { "product_id": 1, "price": 100, "variant_id": 100 },
            { "product_id": 1, "price": 100, "variant_id": null }
        ]
    }`

    productData := &ProductData{}
    err := json.Unmarshal([]byte(productJson), &productData)
    if err != nil {
        panic(err)
    }

    b, err := json.Marshal(map[string]interface{}{"purchased_products": productData.Products})
    if err != nil {
        panic(err)
    }

    d := &Response{
        Json:      b,
        CreatedAt: time.Now().Format("2006-01-02 15:04:05"),
        UpdatedAt: time.Now().Format("2006-01-02 15:04:05"),
    }

    out, err := json.Marshal(d)
    if err != nil {
        panic(err)
    }

    log.Println(string(out))
}

Output:

2009/11/10 23:00:00 {"json":{"purchased_products":[{"product_id":1,"price":100,"variant_id":100},{"product_id":1,"price":100,"variant_id":0}]},"created_at":"2009-11-10 23:00:00","updated_at":"2009-11-10 23:00:00"}
Gopher
  • 721
  • 3
  • 8