W przypadku przesyłania obiektu przez interfejs API, jak w formacie JSON bez schematu, jaki jest idealny sposób na zwrócenie nieistniejącej właściwości ciągu? Wiem, że można to zrobić na różne sposoby, jak w przykładach w wymienionych poniżej linkach.
Jestem pewien, że w przeszłości używałem wartości null, ale nie mam dobrego powodu, aby to robić. Używanie wartości null w przypadku bazy danych wydaje się proste. Baza danych wydaje się jednak szczegółem implementacji, który nie powinien dotyczyć strony po drugiej stronie interfejsu API. Np. Prawdopodobnie używają magazynu danych bez schematu, który przechowuje tylko właściwości z wartościami (inne niż null).
Z punktu widzenia kodu ograniczenie funkcji łańcuchowych do pracy tylko z jednym typem, tj. string
(Nie zerowym), ułatwia ich sprawdzenie; unikanie wartości null jest również powodem posiadania Option
obiektu. Więc jeśli kod, który generuje żądanie / odpowiedź nie używa wartości null, domyślam się, że kod po drugiej stronie interfejsu API nie będzie zmuszony do używania wartości null.
Raczej podoba mi się pomysł użycia pustego łańcucha jako łatwego sposobu na uniknięcie wartości null. Jednym argumentem, który usłyszałem o użyciu wartości null i przeciwko pustemu ciągowi jest to, że pusty ciąg oznacza, że właściwość istnieje. Chociaż rozumiem różnicę, zastanawiam się również, czy jest to tylko szczegół implementacji i czy użycie pustego lub pustego łańcucha robi jakąkolwiek rzeczywistą różnicę. Zastanawiam się także, czy pusty ciąg znaków jest analogiczny do pustej tablicy.
Więc jaki jest najlepszy sposób rozwiązania tych problemów? Czy zależy to od formatu przesyłanego obiektu (schemat / schemat)?
źródło
if( value === null) { use parent value; }
Jeśli jednak ustawisz wartość podrzędną, nawet na pusty ciąg znaków (np. Zastąpisz domyślną wartość nadrzędną spacją), to w jaki sposób „ponownie odziedziczysz” tę wartość? Dla mnie ustawienie wartości null oznaczałoby „odznacz tę wartość, abyśmy wiedzieli, że należy użyć wartości nadrzędnej”.null
(prawdą jest, żenull
jest to omijane jako takie), pytający oznacza „Zwróć wartość inną niż null” [Obiekt] (tj .: pusty ciąg, pusta tablica itp.), Gdy napisz „unikaj”.Odpowiedzi:
TLDR; Usuń właściwości null
Pierwszą rzeczą, o której należy pamiętać, jest to, że aplikacje na ich brzegach nie są obiektowe (ani funkcjonalne, jeśli programują w tym paradygmacie). Otrzymany JSON nie jest przedmiotem i nie powinien być traktowany jako taki. To tylko uporządkowane dane, które mogą (ale nie muszą) przekształcić w obiekt. Zasadniczo żaden przychodzący JSON nie powinien być zaufany jako obiekt biznesowy, dopóki nie zostanie zweryfikowany jako taki. Sam fakt, że został zdezrializowany, nie czyni go ważnym. Ponieważ JSON ma również ograniczone prymitywy w porównaniu do języków zaplecza, często warto utworzyć DTO z dopasowaniem JSON dla danych przychodzących. Następnie użyj DTO, aby zbudować obiekt biznesowy (lub próbę błędu) w celu uruchomienia operacji API.
Gdy patrzysz na JSON jako na format transmisji, sensowniejsze jest pominięcie właściwości, które nie są ustawione. Przesyłanie za pośrednictwem drutu jest mniejsze. Jeśli Twój język zaplecza nie używa domyślnie wartości NULL, prawdopodobnie możesz skonfigurować swój deserializator tak, aby wyświetlał błąd. Na przykład moja wspólna konfiguracja dla Newtonsoft.Json tłumaczy wartości null / brakujące właściwości
option
tylko na typy F # i w przeciwnym razie wystąpi błąd. Daje to naturalną reprezentację, które pola są opcjonalne (te zoption
typem).Jak zawsze uogólnienia prowadzą do tej pory. Prawdopodobnie zdarzają się przypadki, w których właściwość domyślna lub null pasuje lepiej. Ale kluczem nie jest spojrzenie na struktury danych na brzegu systemu jako obiekty biznesowe. Obiekty biznesowe powinny mieć gwarancje biznesowe (np. Podać co najmniej 3 znaki) po pomyślnym utworzeniu. Ale struktury danych oderwane od drutu nie mają żadnych rzeczywistych gwarancji.
źródło
Aktualizacja: Trochę zredagowałem odpowiedź, ponieważ mogła prowadzić do zamieszania.
Przejście z pustym ciągiem jest definitywnym „nie”. Pusty ciąg nadal jest wartością, jest po prostu pusty. Brak wartości należy oznaczyć przy konstrukt, który reprezentuje niczego
null
.Z punktu widzenia dewelopera API istnieją tylko dwa typy właściwości:
null
.To wyjaśnia, że gdy właściwość jest obowiązkowa, tj. wymagane, nigdy nie będzie
null
.Z drugiej strony, jeśli opcjonalna właściwość obiektu nie zostanie ustawiona i pozostanie pusta, wolę zachować je w odpowiedzi i tak z
null
wartością. Z mojego doświadczenia ułatwia klientom API implementowanie analizy składniowej, ponieważ nie są oni zobowiązani do sprawdzania, czy właściwość faktycznie istnieje, czy nie, ponieważ zawsze tam jest, i mogą po prostu przekonwertować odpowiedź na niestandardowe DTO, traktującnull
wartości jako opcjonalne.Dynamiczne włączanie / usuwanie pól z sił odpowiedzi, w tym dodatkowe warunki na klientach.
Tak czy inaczej, niezależnie od tego, który wybierzesz, upewnij się, że jest spójny i dobrze udokumentowany. W ten sposób tak naprawdę nie ma znaczenia, czego używasz w interfejsie API, o ile zachowanie jest przewidywalne.
źródło
null
do odwołań. Jednym z moich problemów jest mieszanie wartości z referencjami. Jak odróżnić pola opcjonalnenull
od nie opcjonalnych pól łańcuchowych onull
wartości? Parsując, czy nie testowanie istnienia właściwości powoduje, że parser jest bardziej delikatny?null
podać, że wartość jest opcjonalna. Tak więc, gdy zdefiniujesz obiekt, który ma nie opcjonalną właściwość ciągu, a konsument nie ma tej właściwości, musi wysłać pusty ciąg dla tej właściwości. Czy to prawda?null
Użycie zależy od aplikacji / językaOstatecznie wybór, czy użyć
null
jako prawidłowej wartości aplikacji, zależy w dużej mierze od twojej aplikacji i języka programowania / interfejsu / krawędzi.Na poziomie podstawowym zaleciłbym próbę użycia różnych typów, jeśli istnieją różne klasy wartości.
null
może być opcją, jeśli twój interfejs na to pozwala i istnieją tylko dwie klasy właściwości, które próbujesz reprezentować. Pominięcie właściwości może być opcją, jeśli pozwala na to Twój interfejs lub format. Nowy typ agregatu (klasa, obiekt, typ komunikatu) może być inną opcją.Na przykład, jeśli jest to język programowania, zadam sobie kilka pytań.
Option
prawdopodobnie będzie lepiej dla twojego projektu interfejsu.Option
prawdopodobnie najlepiej wypełnia ten przypadek, jeśli ciąg nie ma wartości zerowej. Jednak prawdopodobnie i tak będziesz musiał sprawdzić dane wejściowe użytkownika pod kątemnull
wartości ciągu, więc prawdopodobnie odłożę się z powrotem do pierwszego wiersza zapytania: ile typów wartości ma / chcę reprezentować.null
oznacza to błąd programisty w moim języku programowania? Niestetynull
często jest domyślną wartością niezainicjowanych (lub nie zainicjowanych jawnie) wskaźników lub odniesień w niektórych językach. Czynull
jako wartość domyślna można zaakceptować wartość? Czy jest to wartość domyślna bezpieczna ? Czasaminull
wskazuje na zwolnione wartości. Czy powinienem informować konsumentów mojego interfejsu o potencjalnych problemach z zarządzaniem pamięcią lub inicjalizacją w ich programie? Jaki jest tryb awarii takiego połączenia w obliczu takich problemów? Czy osoba dzwoniąca jest w tym samym procesie lub wątku co moja, więc takie błędy stanowią duże ryzyko dla mojej aplikacji?W zależności od odpowiedzi na te pytania prawdopodobnie będziesz w stanie dopracować, czy
null
Twój interfejs jest odpowiedni.Przykład 1
null
jest to możliwa wartość ciągu przekazywana z powrotem w przypadku nieprzydzielenia miejsca na ciąg.Odpowiedź:
null
prawdopodobnie nie jest właściweUzasadnienie:
null
w tym przypadku jest faktycznie używane do wskazania dwóch różnych typów wartości. Pierwsza może być wartością domyślną, którą użytkownik interfejsu może chcieć ustawić. Niestety druga wartość jest flagą wskazującą, że system nie działa poprawnie. W takich przypadkach prawdopodobnie chcesz zawieść tak bezpiecznie, jak to możliwe (cokolwiek to oznacza dla twojego systemu).Przykład 2
char *
członka.NULL
char *
członka dla twojego API można wskazać pojedynczą wartościąNULL
char *
członka.Odpowiedź:
NULL
może być odpowiedniUzasadnienie: Istnieje niewielka szansa, że Twoja struktura przejdzie
NULL
test, ale nie zostanie zainicjowana. Jednak Twój interfejs API może nie być w stanie to uwzględnić, chyba że masz jakąś sumę kontrolną wartości struktury i / lub sprawdzania zakresu adresu struktury. Lintery MISRA-C mogą pomóc użytkownikom interfejsu API, oznaczając użycie struktur przed ich inicjalizacją. Jednakże, tak jak w przypadkuchar *
elementu, jeśli wskaźnik do struct wskazuje na zainicjalizowaną strukturę,NULL
jest wartością domyślną nieokreślonego elementu w inicjalizatorze struktury. DlategoNULL
może służyć jako bezpieczna, domyślna wartość dla elementuchar *
struct w aplikacji.Jeśli jest na interfejsie serializacji, zadałbym sobie następujące pytania dotyczące tego, czy użyć null w ciągu znaków.
null
wskazuje na potencjalny błąd po stronie klienta? W przypadku JSON w JavaScript jest to prawdopodobnie nie, ponieważnull
niekoniecznie jest używany jako wskaźnik niepowodzenia alokacji. W JavaScript jest używany jako wyraźne wskazanie braku obiektu w referencji, która ma być problematycznie ustawiona. Istnieją jednak parsery i serializatory inne niż javascript, które mapują JSONnull
nanull
typ macierzysty . W takim przypadku rozpoczyna się dyskusja na temat tego, czynull
użycie natywne jest odpowiednie dla danego połączenia języka, analizatora składni i serializatora.null
oznacza, że masz całkowicie nowy typ wiadomości. Dla klientów korzystających z formatu serializacji może być czystsze określenie tylko zupełnie innego rodzaju komunikatu. Zapewnia to, że ich sprawdzanie poprawności i logika aplikacji mogą mieć czystą separację między dwoma wyróżnieniami komunikatów udostępnianymi przez interfejs sieciowy.Ogólna rada
null
nie może być wartością na krawędzi lub interfejsie, który jej nie obsługuje. Jeśli używasz czegoś, co jest bardzo luźne w naturze przy wpisywaniu wartości właściwości (np. JSON), spróbuj wypchnąć jakąś formę schematu lub walidację w oprogramowaniu krawędziowym konsumenta (np. Schemat JSON ), jeśli możesz. Jeśli jest to interfejs API języka programowania, sprawdź poprawność wprowadzanych danych przez użytkownika, jeśli to możliwe (przez pisanie na klawiaturze) lub tak głośno, jak to możliwe w środowisku wykonawczym (czyli ćwicz programowanie obronne na interfejsach konsumenckich). Co ważne, udokumentuj lub zdefiniuj krawędź, aby nie było wątpliwości, czy:źródło
Oto moja osobista analiza tych pytań. Nie jest poparta żadną książką, papierem, opracowaniem czy czymkolwiek innym, tylko moim osobistym doświadczeniem.
Puste ciągi jak
null
To dla mnie nie do przyjęcia. Nie mieszaj semantyki pustych łańcuchów z tymi, które nie są zdefiniowane. W wielu przypadkach mogą one być idealnie wymienne, ale możesz natknąć się na przypadki, w których niezdefiniowane i zdefiniowane-ale-puste oznaczają coś innego.
Głupi przykład: powiedzmy, że istnieje atrybut, który przechowuje klucz obcy, a ten atrybut nie jest zdefiniowany lub jest
null
, co oznaczałoby, że nie ma zdefiniowanej relacji, podczas gdy pusty ciąg""
może być rozumiany jako zdefiniowana relacja i Identyfikatorem zagranicznego rekordu jest ten pusty ciąg.Nie zdefiniowano vs
null
To nie jest czarny lub biały temat. Oba podejścia mają wady i zalety.
Na rzecz jednoznacznego zdefiniowania
null
wartości istnieją następujące zalety:Za przyjęciem nieistniejącego klucza odpowiada semantyka
null
:W przypadku, gdy interfejs API jest w jakiś sposób stabilny i dokładnie go udokumentujesz, myślę, że można całkowicie stwierdzić, że nieistniejący klucz jest równy znaczeniu
null
. Ale jeśli jest bardziej chaotyczny i chaotyczny (jak to często bywa), myślę, że możesz uniknąć bólów głowy, jeśli wyraźnie zdefiniujesz każdą wartość w każdej wiadomości. Tj. W razie wątpliwości staram się podążać za podejściem pełnym.To powiedziawszy, najważniejsza rzecz: jasno określ swoje intencje i bądź konsekwentny. Nie rób jednej rzeczy tutaj, a drugiej tam. Oprogramowanie przewidywalne to lepsze oprogramowanie.
źródło
null
. Kolejny przykład: w grze istnieje obiekt postaci i atrybut „partner”.null
Partnerem wyraźnie oznacza, że nie ma partnera w ogóle, ale""
można zrozumieć, ponieważ nie jest partnerem, którego nazwa jest""
.null
na pusty ciąg. Może renderowanie w formie, ale nigdy w modelu danych.name
, o którym mówiłem. Czy pozwoliłbyś, aby nazwa partnera była zerowa?Podałbym pusty ciąg w sytuacji, gdy łańcuch jest obecny, a zdarza się, że jest to pusty ciąg. Podałbym wartość null w sytuacji, w której chcę wyraźnie powiedzieć „nie, tych danych nie ma”. I pomiń klucz, aby powiedzieć „brak danych, nie przejmuj się”.
Ty oceniasz, która z tych sytuacji może się zdarzyć. Czy ma sens, aby Twoja aplikacja miała puste ciągi znaków? Czy chcesz rozróżnić między jawnym mówieniem „brak danych” przy użyciu wartości null i domyślnie przez brak wartości? Powinieneś mieć obie możliwości (zero i brak klucza) tylko wtedy, gdy klient musi je rozróżnić.
Teraz pamiętaj, że chodzi o przesyłanie danych. Odbiorca robi z danymi swoją działalność i zrobi to, co jest dla nich najwygodniejsze. Odbiornik powinien być w stanie obsłużyć wszystko, co na niego rzucisz (być może poprzez odrzucenie danych) bez awarii.
Jeśli nie ma żadnych innych względów, przesyłam to, co jest najwygodniejsze dla nadawcy, i dokumentuję to. Wolałbym wcale nie wysyłać nieobecnych wartości, ponieważ prawdopodobnie poprawi to szybkość kodowania, przesyłania i analizowania JSON.
źródło
Chociaż nie mogę powiedzieć, co jest najlepsze , prawie na pewno nie jest to prosty szczegół implementacji , zmienia on strukturę interakcji z tą zmienną.
Jeśli coś może mieć wartość NULL , powinieneś zawsze traktować to tak, jakby w pewnym momencie miało ono wartość NULL , więc zawsze będziesz mieć dwa przepływy pracy , jeden dla wartości NULL i jeden dla prawidłowego ciągu. Podzielony przepływ pracy niekoniecznie jest złą rzeczą, ponieważ istnieje całkiem sporo obsługi błędów i specjalnych przypadków, które można wykorzystać, ale powoduje to zaciemnienie kodu.
Jeśli zawsze będziesz wchodził w interakcje z ciągiem w ten sam sposób , prawdopodobnie funkcjonalność pozostanie w twojej głowie .
Podobnie jak w przypadku każdego pytania „co jest najlepsze”, pozostaje mi odpowiedź: to zależy . Jeśli chcesz podzielić przepływ pracy i wyraźniej uchwycić, gdy coś nie jest ustawione, użyj null. Jeśli wolisz, aby program nadal działał tak, jak robi, użyj pustego ciągu. Ważne jest, abyś był konsekwentny , wybierał wspólny zwrot i trzymał się tego.
Biorąc pod uwagę, że tworzysz interfejs API, zalecałbym trzymanie się pustego ciągu, ponieważ użytkownik ma mniej do zrekompensowania, ponieważ jako użytkownik interfejsu API nie znam wszystkich powodów, dla których interfejs API może dać mi wartość zerową, chyba że „ są bardzo dobrze udokumentowane, czego niektórzy użytkownicy i tak nie przeczytają.
źródło
string
s, nigdy null. Jeśli interfejs API używa wartości null, to w pewnym momencie producent musi go utworzyć,null
aby był zgodny z interfejsem API. Wtedy konsument też musi się tym zająćnull
. Ale myślę, że mam to, co mówisz, po prostu zdecyduj i zdefiniuj API z autorytetem, prawda? Czy to oznacza, że nie ma w tym nic złego?Dokument!
TL; DR>
Rób, co uważasz za stosowne - czasem kontekst, w którym jest używany, ma znaczenie. Przykład: Powiązanie zmiennych do Oracle SQL: Pusty ciąg interpretowany jest jako NULL.
Po prostu powiedziałbym - upewnij się, że udokumentowałeś każdy wymieniony scenariusz
Twój kod może działać na różne sposoby - udokumentuj reakcję kodu:
Oprócz tego - od Ciebie zależy, czy będziesz się zachowywał konsekwentnie i ewentualnie zastosował niektóre z twoich najlepszych praktyk. Dokumentuj to spójne zachowanie. Przykłady:
źródło
tl; dr - jeśli go użyjesz: bądź konsekwentny w jego znaczeniu.
Jeśli to podasz
null
, co by to oznaczało? Istnieje wszechświat rzeczy, co to może znaczyć. Jedna wartość to po prostu za mało, aby przedstawić brakującą lub nieznaną wartość (a są to tylko dwie z niezliczonych możliwości: np. Brakujące - zostało zmierzone, ale jeszcze tego nie wiemy. Nieznane - nie próbowaliśmy mierzyć to.)W przykładzie, na który natknąłem się ostatnio, pole może być puste, ponieważ nie zgłoszono, aby chronić czyjąś prywatność, ale było znane po stronie nadawcy, nieznane po stronie nadawcy, ale znane pierwotnemu reporterowi lub nieznane obu stronom. I wszystko to miało znaczenie dla odbiornika. Tak więc zwykle jedna wartość nie wystarcza.
Przy założeniu, że świat jest otwarty (po prostu nie wiesz o rzeczach nie powiedzianych), po prostu pominiesz go i może to być cokolwiek. Przy założeniu, że zamknięty świat (rzeczy nie wymienione są fałszywe, na przykład w SQL) lepiej wyjaśnij, co
null
oznacza, i zachowaj zgodność z tą definicją, jak możesz ...źródło