Распаковка вложенных JSON объектов в Go: проблема с пустыми полями
Почему поля Details остаются пустыми?
Ваш код в целом правильный для распаковки вложенных JSON, но проблема скорее всего в том, как вы передаёте JSON. В Go json.Unmarshal требует, чтобы JSON был в виде []byte. Если вы читаете JSON из файла или строки, убедитесь, что конвертируете его правильно. Вот рабочий пример:
package main
import (
"encoding/json"
"fmt"
"log"
)
type Person struct {
ID int `json:"id"`
Name string `json:"name"`
Details Details `json:"details"`
}
type Details struct {
Age int `json:"age"`
City string `json:"city"`
}
func main() {
jsonString := `{
"id": 123,
"name": "Test",
"details": {
"age": 30,
"city": "Moscow"
}
}`
var person Person
err := json.Unmarshal([]byte(jsonString), &person)
if err != nil {
log.Fatal(err)
}
fmt.Printf("ID: %d\n", person.ID)
fmt.Printf("Name: %s\n", person.Name)
fmt.Printf("Age: %d\n", person.Details.Age)
fmt.Printf("City: %s\n", person.Details.City)
}
Этот код выведет:
ID: 123
Name: Test
Age: 30
City: Moscow
Возможные причины вашей проблемы:
- Опечатки в JSON-тегах: проверьте, что теги в структурах точно соответствуют ключам в JSON (регистр имеет значение).
- Лишние пробелы или символы: если JSON приходит из файла, там могут быть BOM-символы или невидимые пробелы.
- Поле details отсутствует или равно null: проверьте сам JSON — возможно, в реальных данных поле
detailsиногда отсутствует.
Что делать, если details может отсутствовать?
Если поле details может быть null или отсутствовать, используйте указатели или omitempty:
type Person struct {
ID int `json:"id"`
Name string `json:"name"`
Details *Details `json:"details"` // указатель
}
// Или используйте отдельный флаг
type Person struct {
ID int `json:"id"`
Name string `json:"name"`
Details Details `json:"details,omitempty"`
}
Работа с динамической вложенностью через map
Если структура JSON может меняться, используйте map[string]interface{}:
var result map[string]interface{}
err := json.Unmarshal([]byte(jsonString), &result)
if err != nil {
log.Fatal(err)
}
// Доступ к вложенным полям
details := result["details"].(map[string]interface{})
age := details["age"].(float64) // числа в JSON всегда float64
fmt.Println(age)
Проверка ошибок — ключ к успеху
Всегда проверяйте ошибку после json.Unmarshal. Если JSON невалидный, вы получите сообщение, которое укажет на проблему. Также можно использовать json.Decoder для потоковой обработки:
decoder := json.NewDecoder(strings.NewReader(jsonString))
var person Person
err := decoder.Decode(&person)
if err != nil {
log.Fatal(err)
}
Этот метод лучше подходит для больших JSON или чтения из сети.
<<Альтернативный подход: использование встроенных структур (embedded structs)
Если вложенный объект логически является частью родительского, можно "встроить" структуру, чтобы поля были на одном уровне:
type Person struct {
ID int `json:"id"`
Name string `json:"name"`
Details // встраивание
}
type Details struct {
Age int `json:"age"`
City string `json:"city"`
}
func main() {
jsonString := `{"id": 123, "name": "Test", "age": 30, "city": "Moscow"}`
var p Person
json.Unmarshal([]byte(jsonString), &p)
fmt.Println(p.Age, p.City) // 30 Moscow
}
Обратите внимание: JSON теперь плоский, но структура остаётся логически разделённой. Это удобно, если вы контролируете формат данных. Для вашего изначального вложенного JSON этот подход не подойдёт — он ожидает, что поля age и city будут на верхнем уровне.
Рекомендация
Для строго типизированной работы с вложенными JSON всегда используйте отдельные структуры (как в первом ответе). Это даёт читаемость, автодополнение в IDE и проверку типов на этапе компиляции. Если структура JSON неизвестна заранее — используйте map[string]interface{}, но будьте готовы к приведению типов.
Ответов пока нет.
Войдите чтобы оставить ответ