2014-09-25 4 views
0

Я учусь, написав простой HTTP-сервер, и мне нужно обработать некоторые ответы JSON.Unmarshalling массив верхнего уровня JSON в Go

С ответом объекта, я могу распаковать его идиоматически 2 строки кода: structResult: = Foo {} json.Unmarshal (structBody, & structResult)

Я не знаю, как сделать то же самое для ответа массива (см. пример ниже). Есть ли способ указать (возможно, через тег json), что массив верхнего уровня должен перейти в заданное поле структуры?

package main 

import "fmt" 
import "encoding/json" 

type Foo struct { 
    Id uint64 `json:"id"` 
    Name string `json:"name"` 
} 

type BaseResult struct { 
    Error string `json:"error"` 
} 

type FooResult struct { 
    BaseResult 
    Foos []Foo 
} 

func main() { 
    // Simple and works. 
    structBody := []byte(`{"id": 1,"name": "foo"}`) 
    structResult := Foo{} 
    json.Unmarshal(structBody, &structResult) 
    fmt.Printf("%#v\n", structResult) 

    // Doesn't work. 
    arrayBody := []byte(`[{"id": 1,"name": "foo"},{"id": 2,"name": "bar"},{"id": 3,"name": "foobar"}]`) 
    arrayResult := FooResult{} 
    json.Unmarshal(arrayBody, &arrayResult) 
    fmt.Printf("%#v\n", arrayResult) 
} 

Я знаю, что я мог бы сделать FooResult массив:

type FooResult []Foo 

но тогда я теряю способность определить базовый объект, который я хотел бы использовать для хранения сообщений об ошибках и тому подобное. Я также знаю, что я могу отменить в & fooResult.Foos напрямую, но я хочу, чтобы код работал как с объектами, так и с массивами.

UPDATE

Внедрение UnmarshalJSON как это было предложено @dyoo частично решает мою проблему, но я надеялся, что я мог бы использовать для хранения BaseResult Ошибка синтаксического анализа в случае JSON имеет другую структуру:

arrayBody := []byte(`{"error": "foo"}`) 
arrayResult := FooResult{} 
json.Unmarshal(arrayBody, &arrayResult) 
fmt.Printf("%#v\n", arrayResult) 

Of Конечно, я мог бы внедрить более сложную логику внутри UnmarshalJSON - но разве нет более простого способа сделать это?

+0

Рекомендация: переименовать '' FooResult' к ArrayResult'. Использование 'Foo', поскольку любая часть имени часто сомнительна, потому что это ничего не значит. – dyoo

+0

Конечно, в этом примере я использовал имя «foo». – proxi

ответ

1

Вы можете реализовать интерфейс json.Unmarshaler в своем FooResult, чтобы точно настроить, как он реагирует на разматывание. (Точно так же, есть интерфейс json.Marshaler.)

Добавить:

func (f *FooResult) UnmarshalJSON(bs []byte) error { 
    return json.Unmarshal(bs, &f.Foos) 
} 

, после чего ваш код должен работать иначе. http://play.golang.org/p/oMdoB2e-rB

Вы могли бы попробовать что-то вроде:

func (f *FooResult) UnmarshalJSON(bs []byte) error { 
    err1 := json.Unmarshal(bs, &f.BaseResult) 
    err2 := json.Unmarshal(bs, &f.Foos) 
    if err1 != nil && err2 != nil { 
     // Arbitrarily choose an error. 
     return err1 
    } 
    return nil 
} 

хотя и это начинает выглядеть сомнительными. Обработка Тип объединения Результаты не совсем то, что библиотека json предназначена для автоматической обработки. Вам нужно будет явно закодировать логику принуждения, если ваш JSON имеет динамический тип.

См.: How to unmarshall an array of different types correctly? и http://blog.golang.org/json-and-go по смежным вопросам.

+0

Спасибо. Это отвечает на мою проблему, но реальная причина для моего вопроса заключалась в том, что я надеялся использовать базовую структуру для хранения ошибки в случае, если возвращаемый JSON имеет другую структуру (многие API-интерфейсы охвачены таким образом). Интересно, возможно ли это. – proxi

+0

Вам не нужно реализовывать UnmarshalJSON, просто укажите Foos, когда вы отмените его.У меня есть пример в моем ответе ниже. – jmaloney

0

Просто укажите Foos когда вы распаковать

package main 

import "fmt" 
import "encoding/json" 

type Foo struct { 
    Id uint64 `json:"id"` 
    Name string `json:"name"` 
} 

type BaseResult struct { 
    Error string `json:"error"` 
} 

type FooResult struct { 
    BaseResult 
    Foos []Foo 
} 

func main() { 
    // Simple and works. 
    structBody := []byte(`{"id": 1,"name": "foo"}`) 
    structResult := Foo{} 
    json.Unmarshal(structBody, &structResult) 
    fmt.Printf("%#v\n", structResult) 

    // Doesn't work. 
    arrayBody := []byte(`[{"id": 1,"name": "foo"},{"id": 2,"name": "bar"},{"id": 3,"name": "foobar"}]`) 
    arrayResult := FooResult{} 
    if err := json.Unmarshal(arrayBody, &arrayResult.Foos); err != nil { 
     arrayResult.BaseResult.Error = string(arrayBody) 
    } 

    fmt.Printf("%#v\n", arrayResult) 
} 
+0

Спасибо. Я знаю, что могу это сделать, но все мое дело в том, чтобы держать парсинг логики близко к структуре и избегать написания кода для каждого другого случая. Решение @ dyoo через UnmarshalJSON лучше. – proxi

Смежные вопросы