Використовуйте порожню рядок, нульову чи видаліть порожню властивість у запиті / відповіді API


25

Під час передачі об'єкта через API, як у безшовному форматі JSON, який ідеальний спосіб повернути неіснуюче властивість рядка? Я знаю, що існують різні способи зробити це, як у прикладах у перелічених нижче посиланнях.

Я впевнений, що раніше я використовував null, але не маю вагомих причин для цього. Мабуть, прямо вперед використовувати null при роботі з базою даних. Але база даних здається детальною інформацією про реалізацію, яка не повинна стосуватися сторони з іншого боку API. Наприклад, вони, ймовірно, використовують безшовний сховище даних, який зберігає лише властивості зі значеннями (ненулеві).

З точки зору коду, обмеження рядкових функцій працювати лише з одним типом, тобто string(не нульовим), спрощує їх доведення; уникнення нуля також є причиною наявності Optionоб’єкта. Отже, якщо код, який створює запит / відповідь, не використовує null, я думаю, що код з іншого боку API також не буде змушений використовувати null.

Мені більше подобається ідея використовувати порожній рядок як простий спосіб уникнути використання нуля. Один з аргументів, який я чув за використання null та проти порожньої рядка, - це те, що порожня рядок означає, що властивість існує. Хоча я розумію різницю, мені також цікаво, чи це лише детальна інформація про реалізацію, і чи використання нульової чи порожньої рядки має різницю в реальному житті. Мені також цікаво, чи порожній рядок є аналогом порожнього масиву.

Отже, який найкращий спосіб зробити це, щоб вирішити ці проблеми? Чи залежить це від формату об'єкта, що передається (схема / без схеми)?


2
Також зауважте, що Oracle трактує порожні рядки та нульові рядки однаково. І ось тут: на паперовому опитувальнику, як ви могли відрізнити відповідь, яку не було, та відповідь, що складається з порожнього рядка?
Бернхард Гіллер

Якщо ви користуєтеся успадкуванням, легко сказати. if( value === null) { use parent value; } Однак якщо ви встановите дочірнє значення навіть на порожній рядок (наприклад, замініть значення батьківського значення за замовчуванням), то як ви "повторно успадковуєте" це значення? Для мене встановлення його як null означало б "скинути це значення, щоб ми знали використовувати батьківське значення".
Френк Форте

Оскільки "Видалити порожню властивість" також є причиною того, щоб "уникати" a null(це правда, що nullуникається як такої), запитуючий означає "Повернути ненулеве" [Об'єкт] (тобто: порожній рядок, порожній масив тощо), коли вони написати "уникати".
cellepo

Відповіді:


18

TLDR; Видаліть нульові властивості

Перше, що слід пам’ятати, - це те, що програми на їхніх краях не є об’єктно-орієнтованими (ні функціональними, якщо програмування в цій парадигмі). Отриманий вами JSON не є об'єктом і не повинен розглядатися як такий. Це просто структуровані дані, які можуть (або не можуть) перетворюватися в об'єкт. Загалом, жодному вхідному JSON не слід довіряти як бізнес-об'єкту, поки він не буде затверджений як такий. Саме той факт, що він десеріалізується, не робить його дійсним. Оскільки JSON також має обмежені примітивів по порівнянні з серверними мовами, часто варто зробити JSON вирівняний DTO для даних, що надходять. Потім використовуйте DTO для побудови бізнес-об’єкта (або спроби помилки) для запуску операції API.

Якщо ви дивитесь на JSON лише як на формат передачі, має сенс опустити властивості, які не встановлені. Менше надсилати по дроту. Якщо ваша мова за замовчуванням не використовує нулі за замовчуванням, ви, ймовірно, можете налаштувати свій десеріалізатор, щоб дати помилку. Наприклад, моя звичайна установка для Newtonsoft.Json переводить нульові / відсутні властивості лише в / з optionтипів F # і в іншому випадку буде помилкою. Це дає природне уявлення про те, які поля необов’язкові (поля з optionтипом).

Як завжди, узагальнення дістають лише вам. Ймовірно, є випадки, коли властивість за замовчуванням або нульова властивість підходить краще. Але ключовим є не дивитися на структури даних на краю вашої системи як на бізнес-об’єкти. При успішному створенні бізнес-об’єкти повинні мати гарантії бізнесу (наприклад, назвати не менше 3 символів) Але структури даних, витягнуті з дроту, не мають реальних гарантій.


3
Хоча більшість сучасних серіалізаторів мають необов’язкові поля, опускання нулів у відповіді не завжди є гарною ідеєю, оскільки це може ввести додаткову складність для обробки нульових полів. Таким чином, це дійсно залежить від регістру , залежно від того, як ваша бібліотека серіалізації обробляє нульові значення, і від того, чи (потенційна) додаткова складність обробки цих нуклеусів дійсно варто заощадити кілька байт на запит. Ви повинні наполегливо працювати, щоб проаналізувати ваші бізнес-випадки.
Chris Cirefice

@ChrisCirefice Так, я вважаю, останній параграф охоплює це. Бувають випадки, коли краще використовувати різні стратегії.
Спікер Кейсі

Я погоджуюся, що JSON використовується лише як формат передачі, це не пропускає об'єкт по дротах типу CORBA. Я також погоджуюся, що властивості можна додавати та видаляти; уявлення можуть змінюватися, елементи управління можуть змінюватися, особливо в Інтернеті.
име96

15

Оновлення: відповідь я трохи відредагував, тому що це може призвести до плутанини.


Перехід із порожньою рядком - остаточний ні. Порожній рядок все ще є значенням, він просто порожній. Значення не повинно бути зазначено , використовуючи конструкцію , яка не представляє нічого null.

З точки зору розробника API, існують лише два типи властивостей:

  • обов'язково (вони ОБОВ'ЯЗКОВО мають значення свого конкретного типу, і НЕ МОЖЕ ніколи бути порожнім),
  • необов'язково (вони МОЖУТЬ містити значення конкретного типу, але МОЖУТЬ також містити null.

Це дає зрозуміти, що коли власність є обов'язковою, тобто. вимагається, цього ніколи не може бути null.

З іншого боку, якщо необов'язкове властивість об'єкта не буде встановлено і залишене порожнім, я вважаю за краще залишити їх у відповіді все одно зі nullзначенням. З мого досвіду, клієнтам API простіше здійснити розбір, оскільки їм не потрібно перевіряти, чи існує властивість насправді чи ні, тому що вона завжди є, і вони можуть просто перетворити відповідь на їх власний DTO, обробляючи nullзначення як необов’язковий

Динамічне включення / видалення полів із сил реагування, включаючи додаткові умови для клієнтів.


У будь-якому випадку, незалежно від того, який із способів ви не обрали, переконайтеся, що ви будете зберігати його послідовно та добре документально. Таким чином, це дійсно не має значення, що ви використовуєте для свого API, якщо поведінка передбачувана.


Так, порожній рядок - це значення, яке я використовую nullдля посилань. Змішування значень із посиланнями - одна з моїх проблем. Як ви відрізняєте необов'язкові поля nullвід необов'язкових рядкових полів, які мають nullзначення? Повторний аналіз, чи не тестування існування властивості робить аналізатор більш крихким?
име96

2
@ imela96 Необов’язкові поля НІКОЛИ не можуть бути нульовими. Якщо щось є необов'язковим, воно ОБОВ'ЯЗКОВО має містити значення (його конкретний тип).
Енді

3
Це. Як частий споживач API, я ненавиджу, коли мені доводиться стикатися з "динамічними" структурами, що повертаються до мене, навіть якщо це у формі необов'язкового поля, яке пропускається. (також багато чого погодився, що велика різниця між ZLS і Null). Я б із задоволенням приймав нульові значення цілий день. Як автор API, одна з моїх цілей - зробити споживання клієнтів максимально безболісним, а це означає завжди очікувати структури даних.
jleach

@DavidPacker тому, якщо я правильно зрозумів, ви використовуєте, nullщоб вказати значення необов’язкове. Отже, коли ви визначаєте об'єкт, який має необов'язкове властивість рядка, а споживач не має цього властивості, він повинен надіслати порожню рядок для цього властивості. Це так?
име96

2
@GregoryNisbet Не робіть цього, будь ласка. В цьому немає сенсу.
Енді

3

null Використання залежить від застосування / мови

Зрештою, вибір використання чи nullдійсного значення програми багато в чому визначається вашим додатком та мовою програмування / інтерфейсом / краєм.

На фундаментальному рівні я рекомендую спробувати використовувати окремі типи, якщо є окремі класи значень. nullце може бути варіант, якщо ваш інтерфейс дозволяє це і є лише два класи властивості, які ви намагаєтесь представляти. Опускання властивості може бути варіантом, якщо ваш інтерфейс чи формат це дозволяє. Новий тип сукупності (клас, об’єкт, тип повідомлення) може бути іншим варіантом.

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

  1. Я планую додавати майбутні типи значень? Якщо так, то Option, можливо, буде краще для вашого інтерфейсу.
  2. Коли мені потрібно перевірити дзвінки споживачів? Статично? Динамічно? Раніше? Після? Зовсім? Якщо ваша мова програмування підтримує його, використовуйте переваги статичного введення тексту, оскільки це дозволяє уникнути кількості коду, який ви повинні створити для перевірки. Optionймовірно, найкраще заповнює цей випадок, якщо ваш рядок був ненульовим. Однак вам, мабуть, доведеться все-таки перевірити введення користувачем на nullпредмет значення рядка, тому я, мабуть, відкладу назад до першого рядка опитування: скільки типів значень потрібно / я хочу представляти.
  3. Чи nullвказує на помилку програміста в моїй програмуванні? На жаль, nullчасто це значення за замовчуванням для неініціалізованих (або не явно ініціалізованих) покажчиків чи посилань на деяких мовах. Чи nullприйнятне значення як значення за замовчуванням? Чи безпечно це як значення за замовчуванням? Інколи nullвказує на розміщені значення. Чи слід надати споживачам мого інтерфейсу вказівку на ці потенційні проблеми управління пам’яттю чи ініціалізацію в їхній програмі? Який режим відмови такого дзвінка на тлі таких проблем? Чи викликає абонент у тому самому процесі або потоці, що і мій, так що такі помилки є високим ризиком для моєї програми?

Залежно від ваших відповідей на ці питання, ви, ймовірно, зможете відточити те, чи nullпідходить ваш інтерфейс чи ні .

Приклад 1

  1. Ваш додаток є критично важливим для безпеки
  2. Ви використовуєте певний тип ініціалізації купи під час запуску, і nullце можливе значення рядка, передане назад у разі неможливості виділення місця для рядка.
  3. Є потенціал, що така струна потрапляє у ваш інтерфейс

Відповідь: nullмабуть, не підходить

Обґрунтування: nullу цьому випадку фактично використовується для позначення двох різних типів значень. Перше може бути значенням за замовчуванням, яке може захотіти встановити користувач вашого інтерфейсу. На жаль, друге значення - це прапор, який вказує на те, що ваша система не працює належним чином. У таких випадках ви, ймовірно, хочете вийти з ладу максимально безпечно (що б це не означало для вашої системи).

Приклад 2

  1. Ви використовуєте структуру C, яка має char *член.
  2. Ваша система не використовує розподіл купи, і ви використовуєте перевірку MISRA.
  3. Ваш інтерфейс приймає цю структуру як вказівник і перевіряє, чи не вказує структура NULL
  4. Значення за замовчуванням та безпечне значення char *для вашого API можна вказати одним значеннямNULL
  5. Після ініціалізації вашого користувача, ви хочете надати користувачеві можливість не явно ініціалізувати його char *.

Відповідь: NULLможе бути доречним

Обґрунтування: Є невеликий шанс, що ваша структура пройде NULLперевірку, але неініціалізована. Однак ваш API може не змогти це врахувати, якщо у вас немає якоїсь контрольної суми на значення структури та / або перевірку діапазону адреси структури. Лінійки MISRA-C можуть допомогти користувачам вашого API, позначивши використання конструкцій перед їх ініціалізацією. Однак, що стосується char *члена, якщо вказівник на структуру вказує на ініціалізовану структуру, NULLце значення за замовчуванням не визначеного члена в ініціалізаторі структури. Тому NULLможе слугувати безпечним за замовчуванням значенням char *члена структури у вашій програмі.

Якщо він знаходиться в інтерфейсі серіалізації, я б задав собі наступні запитання щодо того, використовувати чи нуль у рядку чи ні.

  1. Чи nullвказує на потенційну помилку клієнта? Для JSON в JavaScript це, мабуть, ні, оскільки nullне обов'язково використовується як вказівка ​​на помилку розподілу. У JavaScript він використовується як явна вказівка ​​відсутності об'єкта у посиланні, що задається проблематично. Однак там є аналізатори без сеансів JavaScript та серіалізатори, які позначають JSON nullна рідний nullтип. Якщо це так, це починає обговорювати питання про те, чи nullнормальне використання мови для вашого конкретного мови, парсера та серіалізатора.
  2. Чи впливає явна відсутність значення властивості більше, ніж значення однієї властивості? Іноді а nullфактично вказує на те, що у вас повністю новий тип повідомлення. Вашим споживачам може бути більш чітким формат серіалізації, щоб просто вказати зовсім інший тип повідомлення. Це гарантує, що їх логіка перевірки та застосування можуть мати чіткий поділ між двома розрізненнями повідомлень, які надає ваш веб-інтерфейс.

Загальні поради

nullне може бути значенням на краю чи інтерфейсі, які не підтримують його. Якщо ви використовуєте щось надзвичайно вільне за своєю суттю під час введення значень властивостей (наприклад, JSON), спробуйте висунути якусь форму схеми чи перевірки на програмне забезпечення для споживачів (наприклад, схема JSON ), якщо можете. Якщо це API мови програмування, перевіряйте введення користувача, якщо це можливо, статично (шляхом набору тексту) або настільки голосно, наскільки це чутно під час виконання (так само практикуйте захисне програмування на інтерфейсах, що стоять перед споживачами). Як важливо, документуйте або визначте край, щоб не виникало жодних питань щодо:

  • Який тип (-ів) цінності приймає дана властивість
  • Які діапазони значень дійсні для даної властивості.
  • Як має бути структурований агрегатний тип Які властивості повинні / повинні / можуть бути наявними у сукупному типі?
  • Якщо це якийсь тип контейнера, скільки елементів може містити або повинен містити контейнер і які типи значень містить контейнер?
  • Який порядок, якщо такий є, повертаються властивості або екземпляри типу контейнера чи агрегатів?
  • Які побічні ефекти існують при встановленні певних значень та які побічні ефекти при читанні цих значень?

1

Ось мій особистий аналіз цих питань. Це не підкріплено жодною книжкою, папером, навчанням чи будь-яким іншим, лише моїм особистим досвідом.

Порожні рядки як null

Це не для мене. Не змішуйте семантику порожнього рядка з не визначеною. У багатьох випадках вони можуть бути абсолютно взаємозамінними, але ви можете зіткнутися з випадками, коли не визначені та визначені, але порожні означають щось інше.

Якийсь дурний приклад: скажімо, є атрибут, який зберігає зовнішній ключ, і цей атрибут не визначений або є null, це означатиме, що не існує жодного відношення, тоді як порожній рядок ""можна розуміти як визначене відношення і Ідентифікатор закордонного запису - це порожня рядок.

Не визначено проти null

Це не чорна чи біла тема. У обох підходів є плюси і мінуси.

На користь чіткого визначення nullцінностей є такі плюси:

  • Повідомлення є більш самоописательними, оскільки ви знайомитесь із усіма ключами, просто переглянувши будь-яке повідомлення
  • Пов’язаний з попереднім пунктом, простіше кодувати та виявляти помилки у споживача даних: легше виявити помилки, якщо ви отримали неправильні клавіші (можливо неправильне написання, можливо, змінено API тощо).

На користь припущення неіснуючого ключа дорівнює семантиці null:

  • Деякі зміни простіші. Наприклад, якщо нова версія схеми повідомлень містить новий ключ, ви можете кодувати споживача інформації для роботи з цим майбутнім ключем, навіть коли виробник повідомлення ще не оновлювався і ще не подає цю інформацію.
  • Повідомлення можуть бути менш багатослівними або коротшими

Якщо API є якимось стабільним, і ви ретельно його документуєте, я вважаю, що цілком нормально констатувати, що неіснуючий ключ дорівнює значенню null . Але якщо це більш безладно і хаотично (як це досить часто), я думаю, ви можете уникнути головних болів, якщо чітко визначити кожне значення у кожному повідомленні. Тобто, якщо є сумніви, я схильний дотримуватися багатослівного підходу.

Все, що сказано, найголовніше: чітко викладіть свої наміри та будьте послідовні. Не робіть тут однієї справи, а іншої там. Прогнозоване програмне забезпечення - це краще програмне забезпечення.


Прикладом використання порожніх рядків є те, що я маю на увазі під деталями реалізації, тобто припускаючи, що API використовується для викриття рядків бази даних. Чи матиме це значення, якщо немає ніякої бази даних і суто для передачі представлень об'єктів?
име96

Це не повинно бути деталізацією реалізації. Мій приклад насправді говорить про ПК, які пов'язані з БД, але те, що я намагався пояснити, це те, що порожня рядок не є нульовою / нічого / null. Інший приклад: у грі є об’єкт символів, і він має атрибут "партнер". nullПартнер явно означає , що немає партнера на всіх, але ""може бути зрозуміле як є партнер , чиє ім'я "".
bgusach

Я добре з нульовою посиланням партнера означає, що немає партнера, а також посилання не є рядком. Але ім'я партнера - це рядок, навіть якщо ви дозволите null як ім'я партнера, чи не захопили б ви цю нуль і замінили її порожнім рядком в якийсь момент?
име96

Якщо партнера немає, я б не міняв на nullпорожній рядок. Можливо візуалізація у формі, але ніколи в моделі даних.
bgusach

Я не мав на увазі, що жоден партнер, партнер не буде об’єктом. Про партнера, про nameякий я говорив, ви дозволите, щоб ім’я партнера було недійсним?
име96

1

Я б поставив порожню рядок у ситуації, коли рядок присутній, і це буває порожній рядок. Я б поставив "null" у ситуації, коли я хочу прямо сказати "ні, цих даних немає". І пропустіть ключ, щоб сказати "ніяких даних там не турбуйте".

Ви судите, яка з цих ситуацій може статися. Чи має сенс для вашої програми мати порожні рядки? Ви хочете розрізняти чітко сказане "немає даних", використовуючи "null" та неявно, не маючи значення? Ви повинні мати обидві можливості (нульовий і немає ключа), лише якщо обидва повинні відрізнятися клієнтом.

Тепер майте на увазі, що це все стосується передачі даних. Те, що одержувач робить з даними, - це їхня справа, і вони будуть робити те, що їм найзручніше. Одержувач повинен мати можливість обробляти все, що ви кидаєте на нього (можливо, відхиляючи дані) без збоїв.

Якщо немає інших міркувань, я б передавав те, що найзручніше для відправника, і документував це. Я вважаю за краще взагалі не надсилати відсутніх значень, оскільки це, ймовірно, підвищить швидкість кодування, передачі та розбору JSON.


Мені подобається ваша думка про "якщо потрібно відрізняти клієнта".
име96

0

Хоча я не можу сказати, що найкраще, це майже напевно не проста деталь реалізації , але це змінює структуру способу взаємодії з цією змінною.

Якщо щось може бути недійсним, завжди слід ставитися до нього так, ніби воно буде нульовим в якийсь момент , тому у вас завжди будуть два робочих потоки , один для нуля, один для дійсного рядка. Розділений робочий процес не обов'язково є поганою справою, оскільки існує досить багато помилок та спеціальних випадків використання, які ви можете використати, але це примушує ваш код.

Якщо ви завжди взаємодієте зі струною однаково , можливо, функціонал буде простіше залишатися в голові .

Як і на будь-яке питання "що найкраще", я залишаю відповідь: це залежить . Якщо ви хочете розділити робочий процес і більш чітко зафіксувати, коли щось не встановлено, використовуйте null. Якщо ви хочете, щоб програма просто продовжувала робити так, як це робиться, використовуйте порожній рядок. Важливим є те, що ви послідовні , вибирайте загальну віддачу та дотримуйтесь цього.

Зважаючи на те, що ви створюєте API, я рекомендую дотримуватися порожній рядок, оскільки користувачеві менше компенсувати, тому що як користувач API я не знаю всіх причин, коли ваш API міг би дати мені нульове значення, якщо ви не ' дуже добре задокументовані, які деякі користувачі все одно не прочитають.


"Розбити робочий процес" погано. Скажімо, з боку виробника все чисто, а рядкові методи повертають лише strings, ніколи не нульові. Якщо в API використовується null, то в якийсь момент виробник повинен створити це, nullщоб відповідати API. Тоді споживач теж повинен впоратися null. Але я думаю, що я зрозумів, що ви говорите, просто вирішіть і визначте API з повноваженнями, правда? Чи означає це, що в жодному з них немає нічого поганого?
име96

Так, все, що ви робите в своєму API, вплине на те, як користувачеві доведеться структурувати свій код, тому, розглядаючи ваш дизайн з точки зору користувача API, ви повинні мати можливість визначити, який спосіб найкращий. Зрештою, це ваш API. Просто будьте послідовними. Тільки ви можете вирішити плюси і мінуси підходу.
Erdrik Ironrose

0

Документ!

TL; DR>

Робіть так, як вважаєте за потрібне - іноді має значення контекст, у якому він використовується. Наприклад, прив'язування змінних до SQL Oracle: Порожня рядок інтерпретується як NULL.

Просто я сказав би - переконайтеся, що ви документуєте кожен згаданий сценарій

  • НУЛЬ
  • Порожній (порожній)
  • Відсутнє (видалено)

Ваш код може діяти на нього різними способами - документуйте, як ваш код реагує на нього:

  • Fail (виняток тощо), можливо, навіть не вдалося перевірити (можливо, перевірений виняток) vs не може правильно впоратися з ситуацією (NullPointerException).
  • Забезпечте розумні дефолти
  • Код поводиться по-різному

Тоді на додаток до цього - саме вам поводитись послідовно і, можливо, приймати деякі ваші власні найкращі практики. Документуйте цю послідовну поведінку. Приклади:

  • Поводьтеся з нульовим і зниклим тим же
  • Поводьтеся з порожнім рядком саме як таким. Тільки у випадку зв'язування SQL це може вважатися порожнім. Переконайтесь, що ваш SQL поводиться послідовно та очікуваним чином.

Проблема полягає в тому, що, не вирішуючи проблем, розбіжності траплялися досить часто. Зважайте на командне середовище, рішення повинно бути командним рішенням, багато разів це означає, що буде аргумент. Коли у вас є кілька команд, кожна команда має право на власні рішення. Я бачив API, що можу лише здогадуватися, що вони реалізовані різними командами, які не згодні між собою. Якщо хтось може погодитись на одне, документування це тривіально.
име96

0

tl; dr - якщо ви його використовуєте: будьте послідовні у тому, що це означає.

Якби ви включили null, що це означатиме? Існує всесвіт речей, що це може означати. Одного значення просто недостатньо для відображення відсутнього або невідомого значення (а це лише дві з безлічі можливостей: наприклад, відсутнє - воно вимірювалося, але ми цього ще не знаємо. Невідомо - ми не намагалися виміряти це.)

У прикладі, який я нещодавно натрапив, поле може бути порожнім, оскільки не повідомлялося про захист конфіденційності когось, але це було відомо на стороні відправника, невідомому стороні відправника, але відомо первісному репортеру або невідомо обом. І все це мало значення для приймача. Тому зазвичай одного значення недостатньо.

З припущенням про відкритий світ (ви просто не знаєте про речі, про які не йдеться), ви просто залишите це, і це може бути чим завгодно. З припущенням закритого світу (речі, які не вказані, є помилковими, наприклад, у SQL), вам краще зрозуміти, що nullозначає, і бути настільки ж узгодженим з цим визначенням, як ви можете ...

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.