почему unmarshal делает тип объекта измененным в golang - PullRequest
0 голосов
/ 23 ноября 2018

Я хочу написать метод mockData, который может принимать несколько типов параметров и возвращать соответствующие объекты на основе его данных json.Код как показано ниже:

func MockData(jsonPath string,v interface{})(interface{},error){
    var ret interface{}
    data,_ := ioutil.ReadFile(jsonPath)    
    switch v.(type) {
    case Req:
        ret = Req{}
        fmt.Printf("\n===before Unmarshal==%T===\n",ret)
        err = json.Unmarshal(data,&ret) 
        if err!=nil{...}
        fmt.Printf("======after unmarshal===%T\n",ret)
    case ...
    default:
        fmt.Printf("error===not match")
   }
   return ret,err
}

Однако, когда я его использую, он паникует.Код, приведенный ниже:

func main(){
    reqJsonPath := /xx/yy/req.json
    obj,err:=test.MockData(jsonFile,Req{})
    if err!=nil{...}
    require := obj.(Req) //panic cant []interface{} to Req
}

и вывод MockData:

===before Unmarshal==Req===
======after unmarshal===[]interface{}

тип объекта изменился после демаршала .и еще более странным является то, что если я заменю:

ret = Req{}

на

ret = &Req{}

, результат будет таким же, как показано ниже:

===before Unmarshal==*Req===
======after unmarshal===*Req

Воспроизвести проблемуболее удобно я даю структуру Require, как показано ниже:

type Req []*Ele
type Ele struct {
   ID   int 
   Level int   
}

summary :

  1. Могу ли я достичь ожидаемой функции, которая производит различные типы объектов на основе ееjson и type?
  2. Почему тип объекта изменился после демаршала и почему он не изменился после добавления &?

Ответы [ 2 ]

0 голосов
/ 23 ноября 2018
  1. Могу ли я получить ожидаемую функцию, которая создает различные типы объектов на основе своего json и типа?

Да, но вам придется преобразовать его обратно, используя утверждение типа ввызывающий конец, т.е.

  MyFoo:=MockData("foo.json", Foo{}).(Foo)

(или несколько return ret.(Foo) return ret.(Bar) в функции)

Почему тип объекта изменился после unmarshal и почему он не изменился после добавления &?

В верхней части источника Unmarshal есть несколько полезных комментариев, а именно

// To unmarshal JSON into a pointer, Unmarshal first handles the case of
// the JSON being the JSON literal null. In that case, Unmarshal sets
// the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into
// the value pointed at by the pointer. If the pointer is nil, Unmarshal
// allocates a new value for it to point to.

и

// To unmarshal JSON into an interface value,
// Unmarshal stores one of these in the interface value:
//
//  bool, for JSON booleans
//  float64, for JSON numbers
//  string, for JSON strings
//  []interface{}, for JSON arrays
//  map[string]interface{}, for JSON objects
//  nil for JSON null

Итак, в первом случае вы отменяете в значении интерфейса (ret объявляется как интерфейс {}) Во втором случае есть указатель на структуру, так чтовот что вы получаете

0 голосов
/ 23 ноября 2018

Могу ли я получить ожидаемую функцию, которая производит различные типы объектов на основе ее json и типа?

func MockData(filename string, v interface{}) (interface{}, error) {

    data, _ := ioutil.ReadFile(filename)
    switch t := v.(type) {
    case Req:
        // t at this point is a Req{}
        err := json.Unmarshal(data, &t)
        return t, err
    }
    return nil, errors.New("unknown type")
}

Я действительно не знаю вашу мотивацию, почему вы должны пройтифактическая структура, а не указатель. Проверьте эту демонстрацию

Почему тип объекта изменился после демаршала и почему он не изменился после добавления &?

Когда вы демаршируетеиспользуя &ret, где ret - интерфейс, вы получаете адрес интерфейса.Следовательно, json.Unmarshal() увидит, что данные поддержки являются интерфейсом, а не указателем на структуру.Тип данных по умолчанию, который json.Unmarshal() будет использовать, - map[string]interface{} для объектов и []interface{} для массивов.

Теперь, если вы удалите маршалинг с помощью ret, где ret равно &Req{}, json.Unmarshal() будетпроверьте, что данные поддержки имеют значение struct, следовательно, он может выполнить демаршализацию с помощью полей структуры.

Edit :

Вы, кажется, смущены указатель на интерфейс , который отличается от интерфейса с указателем .Попробуйте этот код, и вы увидите разницу.

var x interface{} = Req{}
var y interface{} = &x
var z interface{} = &Req{}

fmt.Printf("%T\n", y)
fmt.Printf("%T\n", z)

Помните, что интерфейсы - это просто нормальные значения, и они также занимают память.Теперь, если вы берете адрес этой памяти, вы получите указатель на интерфейс, а не указатель на данные, на которые ссылается интерфейс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...