2
votes

I have the following structs

type Product struct {
    ID int `json:"id"`
    Name string `json:"name"`
    Dimension []Dimensions `json:"dimensions"`
}

type Dimensions struct {
     Length int `json:"length"`
     Width int `json:"width"`
     Height int `json:"height"`
}

The data is stored flattened, where for each set of Dimensions the ID and Name are duplicated. So for ID 1 and Name ABC there were 2 sets of dimensions. In other words for ID and Name there is a 1 to Many relationship with sets of dimensions.

ID Name Length Width Height
1 ABC 1 2 3
1 ABC 4 5 6

How can I retrieve the with a StructScan or some other method so that the dimensions are nested in the array properly?

1
Don't you mean unmarshal here? You want to read in some JSON to a Product? It says "How can I retrieve" so I assume that's what you want.Suzu Hirose
@SuzuHirose the question contains the sqlx tag and mentions StructScan so I'm quite certain this has nothing to do with JSON unmarshaling.mkopriva
@mkopriva OK thanks for clarifying this.Suzu Hirose
I'm leaving the answer here since it may be partly relevant, if there is no clever way to do it the method I used is probably OK.Suzu Hirose
Sorry to clarify, I want to read the queried rows into the struct. The marshalling to json isn't an issue wants the struct is populated.user3277752

1 Answers

0
votes

I don't think there is an easy way to do this.

The following uses a custom unmarshaller, a custom type, and finally a sorting routine to do the job. You could also do the job within the unmarshaller if you used []Product as the type to unmarshal, but the following is effective.

package main

import (
    "encoding/json"
    "fmt"
    "os"
    "sort"
)

type Product struct {
    ID        int          `json:"id"`
    Name      string       `json:"name"`
    Dimension []Dimensions `json:"dimensions"`
}

type Dimensions struct {
    Length int `json:"length"`
    Width  int `json:"width"`
    Height int `json:"height"`
}

var stuff = `[
{"id":1,"name":"abc","length":1,"width":2,"height":3},
{"id":1,"name":"abc","length":4,"width":5,"height":6},
{"id":2,"name":"pqr","length":4,"width":5,"height":6},
{"id":3,"name":"xyz","length":1,"width":2,"height":3},
{"id":3,"name":"xyz","length":4,"width":5,"height":6},
{"id":3,"name":"xyz","length":7,"width":8,"height":9}
]`

type PseudoProduct struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

func (p *Product) UnmarshalJSON(j []byte) (err error) {
    var d Dimensions
    err = json.Unmarshal(j, &d)
    if err != nil {
        return err
    }
    var q PseudoProduct
    err = json.Unmarshal(j, &q)
    if err != nil {
        return err
    }
    p.Name = q.Name
    p.ID = q.ID
    p.Dimension = append(p.Dimension, d)
    return nil
}

type ProdList []Product

func (pl ProdList) Len() int {
    return len(pl)
}
func (pl ProdList) Less(i, j int) bool {
    pi := pl[i]
    pj := pl[j]
    return pi.ID < pj.ID || pi.Name < pj.Name
}
func (pl ProdList) Swap(i, j int) {
    pl[i], pl[j] = pl[j], pl[i]
}

func main() {
    var products []Product
    err := json.Unmarshal([]byte(stuff), &products)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error unmarshalling: %s\n", err)
        os.Exit(1)
    }
    sort.Sort(ProdList(products))
    n := len(products)
    newproducts := make([]Product, 0)
    for i := 0; i < n; i++ {
        pi := products[i]
        for j := i + 1; j < n; j++ {
            pj := products[j]
            if pj.ID != pi.ID || pj.Name != pi.Name {
                break
            }
            pi.Dimension = append(pi.Dimension, pj.Dimension[0])
            i++
        }
        newproducts = append(newproducts, pi)
    }
    fmt.Printf("%v\n", newproducts)
}

Playground: https://go.dev/play/p/Sv-5dtVmBlw