Изменение функциональности демаршаллинга в зависимости от того, когда объект преобразуется в указатель (иногда указатель, иногда сопоставление) - PullRequest
0 голосов
/ 14 января 2019

Я реализую некоторые методы для сериализации объектов (которые, вероятно, уже существуют в стандартной библиотеке, но я хочу опыт). Мне удалось заставить json.Marshal и json.Unmarshal правильно преобразовать мои данные в строку и затем прочитать обратно в объект, но я заметил интересное поведение, которое я хочу лучше понять.

Чтобы проверить функции пакета json, я создал тестовую структуру:

type Test struct{
    T int
    S string
    F float32
}

Запуск

o := Test{32, "struct", 12.07}
b, e := json.Marshal(o)
fmt.Println(string(b), e)

печать

{"T":32,"S":"struct","F":12.07} <nil>

что я и ожидал. Тем не менее, я могу получить два разных результата при демаршаллинге, в зависимости от того, когда я конвертирую объект в указатель:

// Test 1
var o2 interface{} = &o
e = json.Unmarshal(b, o2)
fmt.Println(o2, e)

печать

// Output 1
&{32 struct 12.07} <nil>

при определении o2 как значения, а не указателя, а затем вызове Unmarshal с & o2, т.е..

// Test 2
var o2 interface{} = o
e = json.Unmarshal(b, &o2)
fmt.Println(o2, e)

печать

// Output 2
map[T:32 S:struct F:12.07] <nil>

Интересно, что все четыре из

// Test 3
var o2 Test = o
e = json.Unmarshal(b, &o2)
fmt.Println(o2, e)

// Test 4
var o2 *Test = &o
e = json.Unmarshal(b, &o2)
fmt.Println(*o2, e)

// Test 5
var o2 *Test = &o
e = json.Unmarshal(b, o2)
fmt.Println(o2, e)

и

// Test 6
var o2 *Test = &o
e = json.Unmarshal(b, &o2)
fmt.Println(o2, e)

выведите одно из следующих значений:

// Output 3, 4
{32 struct 12.07} <nil>

// Output 5, 6
&{32 struct 12.07} <nil>

Тесты 1 и 2 наводят меня на мысль, что некоторая информация времени выполнения теряется, когда что-то присваивается «суперклассу» ... кажется, что присвоение указателя на мою структуру для o2 дает функции Unmarshal больше данных о типе должен прочитать, чем присвоить мою структуру o2, а затем передать указатель на o2 Unmarshal. Это имеет немного смысла; во время компиляции Тест 1 передает интерфейс {} Unmarshal, в то время как Тест 2 передает указатель на интерфейс {}. Тем не менее, я бы подумал, что тип аргумента во время выполнения был бы таким же (* Test). Может кто-нибудь объяснить, почему Go работает таким образом?

Подтверждением моего вывода является тот факт, что объявление o2 в качестве теста или * теста позволяет Unmarshal (который таким образом получает * Test в качестве аргумента) всегда читать мои данные как структуру, а не как карту.

Странно, но Тест 4 и Тест 6 показывают, что передача указателя на указатель на мою структуру полностью приемлема, и Unmarshal правильно (?) Устанавливает значение структуры. Я ожидал бы ошибку времени выполнения для этих тестов, поскольку Unmarshal должен был попытаться разыменовать двойной указатель и установить результирующий указатель на сериализованную структуру, которую он читал. Автоматическая разыменование с несколькими указателями может быть просто функцией этой функции, но это не было упомянуто в официальной документации. Я не слишком обеспокоен этими последними тестами. Меня больше всего интересует, что будет причиной различий между Тестом 1 и Тестом 2.

** Отредактировано 14 января 2018 года, чтобы изменить второй json.Marshal на json. Удалите неправильные скобки из кода копирования / вставки

1 Ответ

0 голосов
/ 14 января 2019

Функция unmarshal проходит по указателям и интерфейсам, содержащим значения указателей, чтобы найти значение назначения. Не указательные значения в интерфейсах игнорируются, потому что эти значения не адресуемые . (В этом описании опущены детали, которые не важны для вопроса).

Если местом назначения является interface{}, а JSON - это объект, то JSON не маршалируется до map[string]interface{}, и это значение сохраняется в интерфейсе. (Это описано в json.Unmarshal документации ).

Тест 1: аргумент unmarshal - *Test. Функция unmarshal проходит через указатель и декодирует в структуру Test.

Тест 2: Аргументом unmarshal является указатель на interface{}, содержащий Test. Функция unmarshal проходит через указатель, чтобы получить interface{}. Значение Test в interface{} игнорируется, поскольку оно не адресуемое . Поскольку местом назначения является interface{}, а JSON является объектом, JSON декодируется в map[string]interface{}.

Код в 3, 4, 5 и 6 не компилируется. Я продолжу, предполагая, что {} следующий Test удален.

var o2 Test = o // 3
var o2 *Test = &o // 4
var o2 *Test = &o // 5
var o2 *Test = &o // 6

Тест 3 и 5: аргумент unmarshal это *Test. Это так же, как # 1.

Тест 4 и 6: аргументом является **Test. Функция unmarshal проходит через указатели и декодирует в структуру Test.

Поскольку у Go нет ничего похожего на «суперкласс», это не проблема.

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