@dimuska139
Backend developer

Как правильно декодировать JSON в UnmarshalJSON?

Есть вот такие 2 структуры, в которые я хочу декодировать JSON, получаемый по API:

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

type Template struct {
	Name            string                `json:"name"`
	CategoryInfo    *TemplateCategoryType `json:"category_info"`
}


При отсутствии category_info в template прилетает вот такое: []. Соответственно я написал свой UnmarshalJSON:

type TemplateCategoryType struct {
	*TemplateCategory
}

func (t *TemplateCategoryType) UnmarshalJSON(b []byte) error {
	s := strings.Trim(string(b), "\"")
	if s == "[]" {
		t.TemplateCategory = nil
		return nil
	}

	var category TemplateCategory
	if err := json.Unmarshal(b, &category); err != nil {
		return err
	}

	t.TemplateCategory = &category
	return nil
}


Но тут возникает проблема, что в template.CategoryInfo у меня получается не просто указатель на структуру, а вот такое &{0xc000134d20} (указатель на структуру TemplateCategoryType, внутри которой указатель на TemplateCategory или же nil) - то есть мне приходится обращаться к данным для получения категории вот так: template.CategoryInfo.CategoryInfo. Как сделать так, чтобы у меня в template.CategoryInfo был либо указатель на структуру, либо просто nil, а не &{nil}?
  • Вопрос задан
  • 116 просмотров
Решения вопроса 2
EvgenyMamonov
@EvgenyMamonov Куратор тега Go
Senior software developer, system architect
Есть не очень красивый вариант, но если нужно быстро решить задачу - можно использовать.
Если у вас нагрузка небольшая - можно его использовать на постоянной основе.
Если же нагрузка огромная - тогда надо иначе делать.
Только везде, где символ `_` надо сделать обработку ошибок.
package main

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

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

type Template struct {
    Name         string            `json:"name"`
    CategoryInfo *TemplateCategory `json:"category_info"`
}

func (t *Template) UnmarshalJSON(b []byte) error {
    var result map[string]interface{}
    if err := json.Unmarshal(b, &result); err != nil {
        return err
    }

    if t == nil {
        t = &Template{}
    }
    t.Name, _ = result[`name`].(string)

    categoryInfo, isMap := result[`category_info`].(map[string]interface{})
    if isMap {
        t.CategoryInfo = &TemplateCategory{}
        t.CategoryInfo.ID, _ = categoryInfo[`id`].(int)
        t.CategoryInfo.Name, _ = categoryInfo[`name`].(string)
    }

    return nil
}

func main() {
    json1 := []byte(`{
        "name": "Мой шаблон",
        "category_info": {
            "id": 109,
            "name": "Тест"
        }
    }`)

    json2 := []byte(`{
        "name": "Мой шаблон",
        "category_info": []
    }`)

    var data1 Template
    err := json.Unmarshal(json1, &data1)
    if err != nil {
        log.Fatalf(`json1: %s`, err)
    }

    var data2 Template
    err = json.Unmarshal(json2, &data2)
    if err != nil {
        log.Fatalf(`json2: %s`, err)
    }

    fmt.Printf("data1: %+v\n", data1)
    fmt.Printf("data1.CategoryInfo: %+v\n\n", data1.CategoryInfo)

    fmt.Printf("\n\ndata2: %+v\n", data2)
    fmt.Printf("data2.CategoryInfo: %+v\n\n", data2.CategoryInfo)
}

Вывод:
data1: {Name:Мой шаблон CategoryInfo:0xc00000c0c0}
data1.CategoryInfo: &{ID:0 Name:Тест}

data2: {Name:Мой шаблон CategoryInfo:}
data2.CategoryInfo:
Ответ написан
Как насчет такого варианта?
https://play.golang.org/p/kqqWgqNacZO
package main

import (
	"encoding/json"
	"fmt"
)

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

type Template struct {
	Name            string           `json:"name"`
	CategoryInfoRaw *json.RawMessage `json:"category_info"`
	CategoryInfo    *TemplateCategory
}

func (t *Template) UnmarshalJSON(data []byte) error {
	type template Template
	if err := json.Unmarshal(data, (*template)(t)); err != nil {
		return err
	}

	if t.CategoryInfoRaw == nil || string(*t.CategoryInfoRaw) == "[]" {
		t.CategoryInfo = nil
	} else {
		if err := json.Unmarshal(*t.CategoryInfoRaw, &t.CategoryInfo); err != nil {
			return err
		}
	}

	return nil
}

func main() {
	res := &Template{}
	str1 := `{
		"name": "Мой шаблон",
		"category_info": {
    			"id": 109,
    			"name": "Тест"
                 }
        }`

	str2 := `{
 		"name": "Мой шаблон",
  		"category_info": []
	}`

	_ = json.Unmarshal([]byte(str1), res)
	fmt.Printf("%+v\n", res)
	fmt.Printf("%+v\n", res.CategoryInfo)
	_ = json.Unmarshal([]byte(str2), res)
	fmt.Printf("%+v", res)
}


Прошу прощение за форматирование - под рукой был только go-playground
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы