Otrzymuję obiekt JSON z wywołania AJAX do serwera REST. Ten obiekt ma nazwy właściwości pasujące do mojej klasy TypeScript (jest to kontynuacja tego pytania ).
Jaki jest najlepszy sposób na jego zainicjowanie? Nie sądzę, żeby to zadziałało, ponieważ klasa (i obiekt JSON) ma członków, które są listami obiektów i członków, które są klasami, a te klasy mają członków, które są listami i / lub klasami.
Ale wolałbym podejście, które sprawdza nazwy członków i przypisuje je, tworząc listy i tworząc instancje w razie potrzeby, więc nie muszę pisać jawnego kodu dla każdego członka w każdej klasie (jest DUŻO!)
json
typescript
David Thielen
źródło
źródło
Odpowiedzi:
Oto kilka szybkich ujęć, które pokazują kilka różnych sposobów. Nie są one w żadnym wypadku „kompletne” i jako wyłączenie odpowiedzialności nie wydaje mi się, aby robić to w ten sposób. Również kod nie jest zbyt czysty, ponieważ napisałem go dość szybko.
Również jako uwaga: Oczywiście klasy deserializowalne muszą mieć domyślne konstruktory, jak ma to miejsce we wszystkich innych językach, w których jestem świadomy wszelkiego rodzaju deserializacji. Oczywiście Javascript nie będzie narzekał, jeśli wywołasz konstruktora, który nie jest domyślny bez argumentów, ale lepiej przygotuj się na klasę (plus, tak naprawdę nie byłby to „typowy sposób”).
Opcja nr 1: Brak informacji o czasie wykonywania
Problem z tym podejściem polega głównie na tym, że nazwa dowolnego członka musi pasować do jego klasy. Co automatycznie ogranicza cię do jednego członka tego samego typu na klasę i łamie kilka zasad dobrej praktyki. Zdecydowanie odradzam to, ale po prostu wymień to tutaj, ponieważ był to pierwszy „szkic”, kiedy napisałem tę odpowiedź (dlatego też imiona to „Foo” itp.).
Opcja nr 2: właściwość name
Aby pozbyć się problemu w opcji nr 1, potrzebujemy informacji o typie węzła w obiekcie JSON. Problem polega na tym, że w Typescript te rzeczy są konstrukcjami czasu kompilacji i potrzebujemy ich w czasie wykonywania - ale obiekty środowiska wykonawczego po prostu nie mają świadomości swoich właściwości, dopóki nie zostaną ustawione.
Jednym ze sposobów jest uświadomienie klasom ich nazw. Tej właściwości potrzebujesz także w JSON. Właściwie potrzebujesz go tylko w Json:
Opcja nr 3: Jawne określenie typów członków
Jak wspomniano powyżej, informacje o typie członków klasy nie są dostępne w środowisku wykonawczym - to znaczy, chyba że je udostępniamy. Musimy to zrobić tylko dla prymitywnych członków i jesteśmy gotowi:
Opcja nr 4: pełna, ale zgrabna droga
Aktualizacja 01.03.2016: Jak zauważyła @GameAlchemist w komentarzach ( pomysł , implementacja ), tak jak w maszynie Typescript 1.7, opisane poniżej rozwiązanie można lepiej napisać przy użyciu dekoratorów klas / właściwości.
Serializacja zawsze stanowi problem i moim zdaniem najlepszym sposobem jest sposób, który po prostu nie jest najkrótszy. Spośród wszystkich opcji wolałbym to, ponieważ autor klasy ma pełną kontrolę nad stanem zdezrializowanych obiektów. Gdybym musiał zgadywać, powiedziałbym, że wszystkie inne opcje, prędzej czy później, wpędzą cię w kłopoty (chyba że Javascript znajdzie sposób na rozwiązanie tego problemu).
Naprawdę, poniższy przykład nie oddaje sprawiedliwości elastyczności. Naprawdę po prostu kopiuje strukturę klasy. Różnica, o której musisz jednak pamiętać, polega na tym, że klasa ma pełną kontrolę nad użyciem dowolnego typu JSON, który chce kontrolować stan całej klasy (możesz obliczyć rzeczy itp.).
źródło
equals
lubtoString
metod (tyle, że zazwyczaj masz je automatycznie generowane). To nie powinno być zbyt trudne, aby napisać generator dladeserialize
gdybyś chciał, ale to po prostu nie mogą być uruchamiane w czasie automatyki.możesz użyć
Object.assign
Nie wiem, kiedy to zostało dodane, obecnie używam Typescript 2.0.2, i wydaje się, że jest to funkcja ES6.tutaj jest
HalJson
oto, co mówi chrom
więc widać, że nie wykonuje rekurencyjnego przypisania
źródło
Object.assign
. Dlaczego zatem mamy dwie odpowiedzi podobne do leksykonu nad tą?Object.assign
nie będzie działać rekurencyjnie i nie będzie tworzył poprawnych typów obiektów, pozostawiając wartości jakoObject
instancje. Chociaż jest to przydatne w przypadku trywialnych zadań, serializacja złożonego typu nie jest z nim możliwa. Na przykład, jeśli właściwość klasy ma niestandardowy typ klasy,JSON.parse
+Object.assign
utworzy instancję tej właściwości doObject
. Skutki uboczne obejmują brakujące metody i akcesoria.Object.assign
, gdzie nadal sprowadza się do pisania zagnieżdżonej instancji przez dłoń. Takie podejście jest odpowiednie dla bardzo prostych obiektów na poziomie samouczka, ale nie do rzeczywistego użytku.TLDR: TypedJSON (działający dowód koncepcji)
Podstawą złożoności tego problemu jest konieczność deserializacji JSON w czasie wykonywania przy użyciu informacji o typie, które istnieją tylko w czasie kompilacji . Wymaga to udostępnienia informacji o typie w czasie wykonywania.
Na szczęście można to rozwiązać w bardzo elegancki i solidny sposób za pomocą dekoratorów i ReflectDecorators :
Informacje o typie nagrywania
Dzięki kombinacji ReflectDecorators i dekoratorów właściwości można łatwo zapisać informacje o typie dotyczące właściwości. Podstawowym wdrożeniem tego podejścia byłoby:
Dla dowolnej właściwości powyższy fragment doda odniesienie do funkcji konstruktora właściwości do
__propertyTypes__
właściwości ukrytej w prototypie klasy. Na przykład:I to wszystko, w czasie wykonywania mamy wymagane informacje o typie, które można teraz przetwarzać.
Przetwarzanie informacji o typie
Najpierw musimy uzyskać
Object
instancję za pomocąJSON.parse
- po tym możemy iterować po wejściach__propertyTypes__
(zebrane powyżej) i odpowiednio utworzyć wymagane właściwości. Należy określić typ obiektu głównego, aby deserializator miał punkt początkowy.Ponownie prosta implementacja tego podejścia byłaby:
Powyższy pomysł ma dużą zaletę deserializacji według typów oczekiwanych (dla wartości złożonych / obiektowych), zamiast tego, co jest obecne w JSON. Jeśli
Person
spodziewany jest aPerson
, to tworzona jest instancja. Z pewnych dodatkowych środków bezpieczeństwa w miejscu dla prymitywnych typów i tablic, takie podejście może być bezpieczne, która jest odporna na dowolny złośliwy JSON.Edge Cases
Jeśli jednak cieszysz się teraz, że rozwiązanie jest tak proste, mam złe wieści: istnieje ogromna liczba przypadkowych spraw, którymi należy się zająć. Tylko niektóre z nich to:
Jeśli nie chcesz się nimi bawić (założę się, że tego nie robisz), chętnie polecę działającą eksperymentalną wersję proof-of-concept wykorzystującą to podejście, TypedJSON - którą stworzyłem aby rozwiązać dokładnie ten problem, problem, z którym codziennie się spotykam.
Ze względu na to, że dekoratorzy wciąż są uważani za eksperymentalne, nie polecałbym używania go do celów produkcyjnych, ale jak dotąd dobrze mi to służyło.
źródło
Używam tego faceta, aby wykonać zadanie: https://github.com/weichx/cerialize
To bardzo proste, ale potężne. To wspiera:
Przykład:
źródło
Stworzyłem narzędzie, które generuje interfejsy TypeScript i „mapę typów” środowiska wykonawczego do przeprowadzania sprawdzania typu środowiska wykonawczego w porównaniu z wynikami
JSON.parse
: ts.quicktype.ioNa przykład, biorąc pod uwagę ten JSON:
quicktype tworzy następujący interfejs TypeScript i mapę typów:
Następnie sprawdzamy wynik
JSON.parse
na podstawie mapy typów:Pominąłem trochę kodu, ale możesz wypróbować quicktype, aby uzyskać szczegółowe informacje.
źródło
Opcja # 5: Korzystanie z konstruktorów maszynopisu i jQuery.extend
Wydaje się, że jest to najbardziej konserwowalna metoda: dodaj konstruktor, który przyjmuje jako parametr strukturę json i rozszerz obiekt json. W ten sposób możesz przeanalizować strukturę json w całym modelu aplikacji.
Nie ma potrzeby tworzenia interfejsów ani wyświetlania właściwości w konstruktorze.
W wywołaniu zwrotnym ajax, w którym otrzymujesz firmę do obliczania wynagrodzeń:
źródło
$.extend
pochodzą?Object.assign
, co usuwa tę zależność od jQuery.W przypadku prostych obiektów podoba mi się ta metoda:
Wykorzystanie możliwości definiowania właściwości w konstruktorze pozwala na zwięzłość.
To daje ci wpisany obiekt (w porównaniu do wszystkich odpowiedzi, które używają Object.assign lub jakiegoś wariantu, który daje ci Object) i nie wymaga zewnętrznych bibliotek ani dekoratorów.
źródło
Czwarta opcja opisana powyżej to prosty i przyjemny sposób, aby to zrobić, którą należy połączyć z drugą opcją w przypadku, gdy trzeba obsłużyć hierarchię klas, na przykład listę członków, która jest jednym z przypadków podklasy superklasa Członka, np. Dyrektor przedłuża Członka lub Student przedłuża Członka. W takim przypadku musisz podać typ podklasy w formacie json
źródło
JQuery .extend robi to za Ciebie:
źródło
najlepsze, co znalazłem w tym celu, to transformator klasy. github.com/typestack/class-transformer
Tak to wykorzystujesz:
Niektóre klasy:
Jeśli użyjesz dekoratora @Type, zostaną również utworzone zagnieżdżone właściwości.
źródło
Może nie rzeczywiste, ale proste rozwiązanie:
pracuj też dla trudnych zależności !!!
źródło
baz
będzie to typ,Object
a nie typBar.
Działa w tym prostym przypadku, ponieważBar
nie ma żadnych metod (tylko prymitywne właściwości). GdybyBar
istniała metoda podobnaisEnabled()
, to podejście nie udałoby się, ponieważ metoda ta nie znalazłaby się w serializowanym ciągu JSON.Inna opcja z wykorzystaniem fabryk
https://github.com/MrAntix/ts-deserialize
używać w ten sposób
źródło
Osobiście wolę opcję nr 3 @Ingo Bürk. Ulepszyłem jego kody, aby obsługiwały tablicę złożonych danych i tablicę prymitywnych danych.
źródło
możesz zrobić jak poniżej
i
źródło
źródło