Если вы объявляете переменную как тип interface{}
, она равна типу interface{}
. Это не является , когда-либо, некоторым значением map[keytype]valuetype
. Но переменная типа interface{}
может содержать значение, которое имеет какой-то другой конкретный тип. Когда он это делает, он делает это - вот и все, что нужно сделать. Он по-прежнему является типом interface{}
, но содержит значение некоторого другого типа.
Значение интерфейса состоит из двух частей
КлючРазличие здесь заключается в том, что interface{}
переменная является , и что она содержит . Любая переменная интерфейса на самом деле имеет два слота внутри: один для хранения того, что тип хранится в нем, и один для хранения того, что значение хранится в нем. Каждый раз, когда вы или кто-либо другой присваиваете значение переменной , компилятор заполняет оба слота: тип, из типа используемого вами значения, и значение из используемого вами значения. 1 Переменная интерфейса сравнивается равной нулю, если она имеет nil
в обоих слотах;и это также нулевое значение по умолчанию.
Следовательно, ваш тест во время выполнения:
valueMap, ok := myMap.(map[string]interface{})
- разумная вещь: если myMap
содержит значение типа map[string]interface
,ok
устанавливается в true
, а valueMap
содержит значение (которое имеет этот тип). Если myMap
содержит значение с другим типом, ok
устанавливается в false
, а valueMap
устанавливается в нулевое значение типа map[string]interface{}
. Другими словами, во время выполнения код сначала проверяет слот типа, затем либо копирует слот значения в valueMap
и устанавливает ok
в true, либо устанавливает valueMap
в nil
и устанавливает ok
в false.
Если и когда ok
было установлено на true
, каждое valueMap[k]
значение равно типу interface{}
. Как и прежде, для самого myMap
каждая из этих interface{}
переменных может - но не обязана - содержать значение типа string
, и вы должны использовать какое-то "что такоефактический тип-значение "во время выполнения теста, чтобы разделить их на части.
Когда вы используете json.Unmarshal
для помещения декодированного JSON в переменную типа interface{}
, он способендесериализации любого из этих документированных типов JSON. Затем список сообщает, что type вставляется в переменную интерфейса:
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
Так что после выполнения json.Unmarshal
в переменную типа interface{}
,Вы должны проверить, какой тип был помещен в слот типа переменной. Вы можете сделать это с помощью утверждения и логического значения ok
, или, если хотите, можете использовать переключатель типа для его декодирования:
var i interface
if err := json.Unmarshal(data, &i); err != nil {
panic(err)
}
switch v := i.(type) {
case string:
... code ...
case map[string]interface{}:
... code ...
... add some or all of the types listed ...
}
Дело в том, что независимо от того, что вы делаете в коде, вы сделали json.Unmarshal
положили что-то в interface{}
, а interface{}
- это типа i
. Вы должны проверить во время выполнения, какую пару типов и значений поддерживает интерфейс.
Другой вариант - вручную проверить строки JSON и решить, какой тип переменной предоставить json.Unmarshal
. Это дает вам меньше кода для записи после Unmarshal
, но больше кода для записи до it.
Здесь есть более полный пример здесь, наигровая площадка Go , в которой используются переключатели типа для проверки результата с json.Unmarshal
Он намеренно неполон, но, думаю, у него достаточно вариантов ввода и вывода, чтобы вы могли понять, как обрабатывать все, учитывая приведенную выше цитату о том, что json.Unmarshal
записывает в переменную типа interface{}
.
1 Конечно, если вы присваиваете один interface{}
из другого interface{}
:
var i1, i2 interface{}
... set i1 from some actual value ...
// more code, then:
i2 = i1
, компилятор просто копирует оба слота из i1
в i2
. Когда вы выполняете: два отдельных слота становятся понятнее, например:
var f float64
... code that sets f to, say, 1.5 ...
i2 = f
, так как это записывает float64
в слот типа, а значение 1.5
в слот значения. Компилятор знает , что f
равен float64
, поэтому настройка типа просто означает "вставить в нее константу". Компилятор не обязательно знает значение из f
, поэтому установка значения является копией любого фактического значения.