Подготовьте объект json из немаршалированных данных

У меня есть такие данные json:

      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}]}

Я структурировал его, используя

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


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

Тогда я использую unmarshal

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

И получить результат вроде

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

Теперь мне нужно преобразовать эти данные в объект json, подобный этому

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

После этого мне нужно назначить его "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"),
}

Как мне это сделать?

5 ответов

Решение

Создайте тип (например, PurchasedProducts), как показано ниже.

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

И инициализируйте переменную типа PurchasedProducts и назначьте ваши немаршалированные продукты приобретенным продуктам, как показано ниже.

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

И преобразовать это []byte array в строку и назначьте его карте, как показано ниже.

          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"),
    }

Вы можете запустить и увидеть полный код здесь .

Для полей, допускающих значение NULL, вы можете использовать указатели, например, если variant_id поле json может быть целым числом или json null, и вы хотите сохранить эту информацию, тогда вы можете изменить Variant_id int к Variant_id *int.

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

Чтобы изменить имена полей json между unmarshal и marshal, вы можете объявить второй Products struct с теми же полями, что и исходный, но со структурными тегами, определяющими желаемые имена полей, тогда, если структуры во всех других отношениях эквивалентны, вы можете конвертировать между ними.

      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
}

Тогда, если p относится к типу Product, вы можете просто преобразовать его в PurchasedProduct вот так:

      pp := PurchasedProduct(p)

Чтобы разгрузить преобразование в процесс маршалинга, вы можете сделать так, чтобы исходные типы реализовали json.Marshaler интерфейс и сделайте преобразование там.

      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))
}

С помощью вышеперечисленного вы можете сделать следующее:

      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

Просто определите еще две структуры, моделирующие второй объект JSON:

      type Pdata2 struct {
    PurchasedProducts []Product2
}

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

В Variation_id поле - это *int введите, потому что ваш требуемый выходной JSON показывает "variation_id": null. Если вы объявите поле простым int, его нулевое значение будет маршалировано в 0.

Затем инициализируйте эти структуры, используя значения из предыдущих:

      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
}

Игровая площадка: https://play.golang.org/p/xhsmHNBjRKN

Вот, пожалуйста. Предположения таковы:

  • Продукт - это модель, которая может быть намного сложнее, поэтому у нее есть специальные структуры. Таким образом, преобразование из Product в OutputProduct можно тестировать отдельно.
  • Это одноразовое приложение, а не часть приложения, предоставляющего API. В противном случае он должен быть правильно разделен на слои, а результат должен быть записан в виде структуры.
      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)
    }
}

На основе предложений из комментариев:

      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))
}

Выход:

      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"}
Другие вопросы по тегам