Библиотека Jackson является мощным инструментом для сериализации и десериализации данных в формат JSON в Java-приложениях. Она предоставляет гибкую и эффективную обработку данных, позволяя преобразовывать Java-объекты в JSON-строки (сериализация) и обратно (десериализация).
Но не всегда процесс десериализации данных в библиотеке Jackson происходит очевидным образом. В этой статье я хочу разобрать несколько примеров не очевидной десериализации Boolean в библиотеке Jackson.
Особенности реализации
В языке Java класс Boolean представляет собой обертку над примитивным типом данных, который может принимать значения: true (истина), false (ложь) или быть равно null. А вы знали, что при десериализации в класс Boolean в JSON можно передавать не только значения true, false и null, но и другие?
Довольно логичным является тот факт, что если передать значения true или false в кавычках, как строку, то оно интерпретируется Jackson как соответствующее значение. А если передать "null", возникнет ли ошибка десериализации?
Hidden text
Нет, ошибки не будет, переменная будет равна null
Jackson так же умеет десериализовать значения "True", "TRUE", "False", "FALSE". Другие варианты написания приведут к ошибке.
Предлагаю продолжить эксперименты, что будет, если передать пустую строку ("")?
Hidden text
Переменная так же будет равна null
Вторым довольно очевидным является то, что можно передавать значения 0 и 1 в JSON, и эти числовые значения успешно десериализуются в класс Boolean, соответственно, 0 преобразуется в false, а 1 - в true.
Но попробуйте предположить, что будет, если передать любое другое целое число?
Hidden text
Любое целое число при десериализации в класс Boolean интерпретируется Jackson как true, независимо от того, является ли оно положительным или отрицательным.
Фичи
Если вам недостаточно описанного выше, то Jackson может предложить вам еще кое-что для десериализации класса Boolean.
Внимание ⚠: Данная функциональность представлена для ознакомления, используйте ее, если точно понимаете, что она делает и к каким последствиям это приведет.
В Jackson есть много разных фич для десериализации, и для нашего Boolean может пригодиться несколько из них. Первая фичa UNWRAP_SINGLE_VALUE_ARRAYS, название говорит само за себя, если в качестве значения передан массив с одним значением, то он разворачивается в это значение. В таком случае, если передать значение:
{
"bool": [-8788888888888888888888888888888]
}
то оно десериализуется в true. Здорово, правда? Будет работать во всех вышеописанных случаях.
Но что, если передать пустой массив, если хочется получить null? Работать не будет, будет ошибка десериализации. А если очень хочется, то можно включить фичу ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, которая, ожидаемо будет приводить пустой массив к null, и наша переменная будет равна именно ему.
Вот и все. Теперь, когда вы осознали эту магию, можете смело использовать это в своих проектах (за исключением последней части). Важно помнить, что при работе с десериализацией Boolean в JSON необходимо быть внимательными и учитывать спецификации и требования вашего проекта. Правильное понимание этих особенностей позволит вам избежать ошибок и создать более надежные и гибкие системы обработки данных.
Комментарии (12)
breninsul
21.06.2023 13:54Если про фичи - они же не относятся конкретно к Boolean, так можно поступить с любым классом (пустой массив как null и массив как значение).
А вот то, что любое число кроме 0 сериализуется в true в поведении по умолчанию - это трындец. Падать должно, это же человек неправильно смапил поле.
mmMike
21.06.2023 13:54+2Вы не поверите в скольких БД и каком количестве кода используется boolean значение как 0/1. Это пошло еще с 70-х годов и корни глубокие.
И сколько лично я видел спецификаций в которых json boolean как "0"/"1" определен.
Так что "не судите и не судимы будете". :)breninsul
21.06.2023 13:54-10/1 понятно, а вот -8700 падать должно
mmMike
21.06.2023 13:54+10/не 0 - стандартная логика переходов в ассемблере. Которая плавно переползла в C, потом в C++ и кое где навечно застряла.
создатели парсера сталкиваются со всем многообразием. Возможно кто то попросил сделать еще и эту логику в ущерб "ой мы тут ошибку допустили и на нее должно ругаться".
Я не знаю причин почему принцип "0/ не 0" перенесен на парсер json. Но явно были причины. Поскольку это не ошибка в коде парсера. А так сделано специально.
TimReset
21.06.2023 13:54Я как-то в jdbc дайвер Oracle залез - там ещё Y и N поддерживается. Помимо true, false, 1, 0. Вроде ещё что-то но тут уже не уверен.
А сделал это когда удивился почему это метод getBoolean ( https://docs.oracle.com/javase/8/docs/api/java/sql/ResultSet.html#getBoolean-int- ) нормально работает хотя в базе у нас был varchar, char, number со значениями 1/0 (в виде числа и символов) и Y/N. И вот там внутри прям много эвристика - типа если в базе число то смотрим на 1 и 0. Если char (один символ) то Y, N, 1, 2. Если varchar ( несколько символов) - то ещё true и false.
Kinski
21.06.2023 13:54+2Собственно, первая часть статьи - обычное поведение слаботипизированного JavaScript (не зря же JSON это JavaScript Object Notation).
Ну а фичи второй части статьи вытекают из первой) Иногда могут быть полезны, но я бы предпочел ловить ошибки, чем разбираться почему массив вдруг превратился в true)
breninsul
21.06.2023 13:54+1по поводу массивов это не поведение по умолчанию.
Бывает полезно когда внешнее апи имеет кривой дизайн и поле приходит массивом
dopusteam
В Java тип boolean может хранить null?
toolateitsme
https://docs.oracle.com/javase/8/docs/api/java/lang/Boolean.html
arionaRu Автор
Есть тип boolean, который может быть только true или false, а есть Boolean, который является оберткой над примитивом и может быть null. При обмене данными с помощью json часто используют второй, т.к. при добавлении параметра в процессе разработки API десериализация в false при отсутствии параметра в запросе (например, не все потребители в курсе, что API изменилось, или им лень дорабатывать) не всегда будет являться корректной.
Спасибо за замечание, исправила слово тип на слово класс, так более корректно.
breninsul
boolean это примитив и не может быть null, а Boolean это класс-враппер boolean, соответственно может быть null как другие обьекты