Zrozumienie serializacji


38

Jestem inżynierem oprogramowania i po dyskusji z kilkoma kolegami zdałem sobie sprawę, że nie mam pojęcia o serializacji koncepcji. Jak rozumiem, serializacja jest procesem przekształcania pewnej jednostki, takiej jak obiekt w OOP, w sekwencję bajtów, dzięki czemu wspomniana jednostka może być przechowywana lub przesyłana w celu późniejszego dostępu (proces „deserializacji”).

Problem, który mam, jest następujący: czy wszystkie zmienne (czy to prymitywne intobiekty, czy obiekty złożone) nie są już reprezentowane przez ciąg bajtów? (Oczywiście, że tak, ponieważ są przechowywane w rejestrach, pamięci, dysku itp.)

Co sprawia, że ​​serializacja jest tak głębokim tematem? Aby serializować zmienną, czy nie możemy po prostu pobrać tych bajtów do pamięci i zapisać je w pliku? Jakie zawiłości przeoczyłem?


21
Serializacja może być trywialna dla sąsiadujących obiektów. Gdy wartość obiektu jest reprezentowana jako wykres wskaźnikowy , sytuacja staje się znacznie trudniejsza, szczególnie jeśli wspomniany wykres ma pętle.
chi

1
@chi: Twoje pierwsze zdanie jest trochę mylące, biorąc pod uwagę, że ciągłość nie ma znaczenia. Możesz mieć wykres, który jest ciągły w pamięci i który nadal nie pomógłby Ci w serializacji, ponieważ nadal musisz (a) wykryć, że rzeczywiście jest on ciągły i (b) naprawić wskaźniki w środku. Powiem tylko drugą część tego, co powiedziałeś.
Mehrdad

@ Mehrdad Zgadzam się, że mój komentarz nie jest całkowicie precyzyjny z powodów, o których wspominasz. Być może lepszym wyróżnieniem jest brak wskaźnika / używanie wskaźnika - nawet jeśli nie jest on całkowicie dokładny
chi

7
Musisz także martwić się reprezentacją na sprzęcie. Jeśli serializuję liczbę int 4 bytesna moim PDP-11, a następnie próbuję odczytać te same cztery bajty do pamięci na moim Macbooku, nie są one tego samego numeru (z powodu Endianes). Musisz więc znormalizować dane do reprezentacji, którą można odkodować (jest to serializacja). Sposób szeregowania danych ma również kompromis między szybkością / elastycznością, że może być odczytany przez człowieka / maszynę.
Martin York,

Co jeśli korzystasz z Entity Framework z wieloma głęboko powiązanymi właściwościami nawigacji? W jednym przypadku możesz chcieć serializować właściwość nawigacji, ale w innym pozostaw ją pustą (ponieważ ponownie załadujesz ten rzeczywisty obiekt z bazy danych na podstawie identyfikatora, który znajduje się w zserializowanym obiekcie nadrzędnym). To tylko przykład. Jest wiele.
ErikE

Odpowiedzi:


40

Jeśli masz skomplikowaną strukturę danych, jej reprezentacja w pamięci może zwykle być rozproszona w całej pamięci. (Pomyśl na przykład o drzewie binarnym.)

W przeciwieństwie do tego, gdy chcesz zapisać go na dysku, prawdopodobnie chcesz mieć reprezentację jako (miejmy nadzieję krótką) sekwencję ciągłych bajtów. To właśnie robi dla Ciebie serializacja.


27

Problem, który mam, jest następujący: czy wszystkie zmienne (czy to prymitywy, takie jak int czy obiekty złożone) nie są już reprezentowane przez sekwencję bajtów? (Oczywiście, że tak, ponieważ są przechowywane w rejestrach, pamięci, dysku itp.)

Co sprawia, że ​​serializacja jest tak głębokim tematem? Aby serializować zmienną, czy nie możemy po prostu pobrać tych bajtów do pamięci i zapisać je w pliku? Jakie zawiłości przeoczyłem?

Rozważmy wykres obiektowy w C z węzłami zdefiniowanymi w ten sposób:

struct Node {
    struct Node* parent;
    struct Node* someChild;
    struct Node* anotherLink;

    int value;
    char* label;
};

//

struct Node nodes[10] = {0};
nodes[5].parent = nodes[0];
nodes[0].someChild = calloc( 1, sizeof(struct Node) );
nodes[5].anotherLink = nodes[3];
for( size_t i = 3; i < 7; i++ ) {
    nodes[i].anotherLink = calloc( 1, sizeof(struct Node) );
}

W czasie wykonywania cały Nodewykres obiektu byłby rozproszony wokół przestrzeni pamięci, a ten sam węzeł mógłby być wskazywany z wielu różnych Węzłów.

Nie można po prostu zrzucić pamięci do pliku / strumienia / dysku i nazwać to serializacją, ponieważ wartości wskaźnika (które są adresami pamięci) nie można zdemerezalizować (ponieważ te lokalizacje pamięci mogą już być zajęte po ponownym załadowaniu zrzutu do pamięci). Kolejnym problemem związanym z po prostu zrzucaniem pamięci jest to, że w końcu będziesz przechowywać różnego rodzaju niepotrzebne dane i nieużywane miejsce - na x86 proces ma do 4 GB pamięci, a system operacyjny lub MMU ma jedynie ogólne pojęcie o tym, jaka jest pamięć znaczące czy nie (na podstawie stron pamięci przypisanych do procesu), więc Notepad.exezrzucenie 4 GB surowych bajtów na mój dysk, gdy chcę zapisać plik tekstowy, wydaje się nieco marnotrawstwem.

Kolejny problem dotyczy wersjonowania: co się stanie, jeśli zserializujesz Nodewykres w dniu 1, a następnie w dniu 2 dodasz kolejne pole Node(takie jak inna wartość wskaźnika lub wartość pierwotna), a następnie w dniu 3 usuniesz serializację pliku z dzień 1?

Musisz także wziąć pod uwagę inne rzeczy, takie jak endianizm. Jednym z głównych powodów, dla których MacOS i pliki IBM / Windows / PC były ze sobą niekompatybilne w latach 80. i 90. XX wieku, mimo że rzekomo były tworzone przez te same programy (Word, Photoshop itp.), Było to, że na liczbach całkowitych x86 / PC w liczbach całkowitych zostały zapisane w kolejności little-endian, ale w big-endian na Macu - a oprogramowanie nie zostało zbudowane z myślą o przenośności między platformami. W dzisiejszych czasach jest lepiej dzięki lepszej edukacji programistów i coraz bardziej heterogenicznemu światowi komputerów.


2
Zrzucenie wszystkiego w przestrzeń pamięci procesu byłoby również okropne ze względów bezpieczeństwa. Noc programu ma w pamięci zarówno 1) niektóre dane publiczne, jak i 2) hasło, tajną nonce lub klucz prywatny. Podczas szeregowania tego pierwszego nie chce się ujawniać żadnych informacji na temat tego drugiego.
chi


15

Trudne jest rzeczywiście już opisane w słowie samego: „ szeregowego zację”.

Pytanie jest w gruncie rzeczy: w jaki sposób mogę przedstawić dowolnie złożony połączony cyklicznie kierowany wykres dowolnie złożonych obiektów jako liniową sekwencję bajtów?

Pomyśl o tym: sekwencja liniowa przypomina rodzaj zdegenerowanego wykresu, w którym każdy wierzchołek ma dokładnie jedną krawędź wejściową i wyjściową (z wyjątkiem „pierwszego wierzchołka”, który nie ma krawędzi wejściowej i „ostatniego wierzchołka”, który nie ma krawędzi wyjściowej) . A bajt jest oczywiście mniej skomplikowany niż obiekt .

Wydaje się więc rozsądne, że gdy przejdziemy od arbitralnie złożonego wykresu do znacznie bardziej ograniczonego „wykresu” (właściwie tylko listy) i od dowolnie złożonych obiektów do prostych bajtów, informacje zostaną utracone, jeśli zrobimy to naiwnie i nie będziemy t w jakiś sposób koduje „obce” informacje. I właśnie to robi serializacja: koduj złożone informacje do prostego formatu liniowego.

Jeśli znasz YAML , możesz rzucić okiem na funkcje zakotwiczenia i aliasu, które pozwalają przedstawić ideę, że „ten sam obiekt może pojawiać się w różnych miejscach” w serializacji.

Np. Jeśli masz następujący wykres:

A → B → D
↓       ↑
C ––––––+

Możesz to przedstawić jako listę ścieżek liniowych w YAML w następujący sposób:

- [&A A, B, &D D]
- [*A, C, *D]

Możesz również przedstawić go jako listę przylegania, macierz przylegania lub jako parę, której pierwszy element jest zbiorem węzłów, a którego drugim elementem jest zestaw par węzłów, ale we wszystkich tych reprezentacjach musisz mieć sposób odwoływania się do istniejących i istniejących węzłów, tzn. wskaźników , których zazwyczaj nie ma w pliku lub strumieniu sieciowym. Ostatecznie wszystko, co masz, to bajty.

(Co BTW oznacza, że ​​powyższy sam plik tekstowy YAML również musi być „zserializowany”, to właśnie są różne kodowania znaków i formaty przesyłania Unicode… nie jest to ściśle „serializacja”, tylko kodowanie, ponieważ plik tekstowy jest już serią / liniowa lista współrzędnych kodowych, ale widać pewne podobieństwa).


13

Inne odpowiedzi już dotyczą złożonych wykresów obiektowych, ale warto zauważyć, że serializacja prymitywów również nie jest trywialna.

Używając nazw typów pierwotnych C dla konkretności, rozważ:

  1. Serializuję a long. Jakiś czas później usuwam serializację, ale ... na innej platformie, a teraz longjest int64_traczej niż int32_tzapisany. Muszę więc bardzo uważać na dokładny rozmiar każdego typu, który przechowuję, lub przechowywać metadane opisujące typ i rozmiar każdego pola.

    Pamiętaj, że ta inna platforma może być tą samą platformą po ponownej kompilacji w przyszłości.

  2. Serializuję int32_t. Jakiś czas później usuwam serializację, ale ... na innej platformie, a teraz wartość jest zepsuta. Niestety zapisałem wartość na platformie big-endian i załadowałem ją na platformę little-endian. Teraz muszę ustalić konwencję dla mojego formatu lub dodać więcej metadanych opisujących endianność każdego pliku / strumienia / czegokolwiek. I, oczywiście, faktycznie wykonuj odpowiednie konwersje.

  3. Serializuję ciąg. Tym razem jedna platforma używa chari UTF-8, a jedna wchar_ti UTF-16.

Twierdzę więc, że serializacja o rozsądnej jakości nie jest trywialna nawet dla prymitywów w ciągłej pamięci. Istnieje wiele decyzji dotyczących kodowania, które musisz udokumentować lub opisać za pomocą wbudowanych metadanych.

Grafy obiektów dodają jeszcze jedną warstwę złożoności.


6

Istnieje wiele aspektów:

Czytelność tego samego programu

Twój program jakoś zapisał twoje dane jako bajty w pamięci. Ale może być dowolnie rozproszony w różnych rejestrach, ze wskaźnikami poruszającymi się tam iz powrotem między jego mniejszymi częściami [edytuj: Jak skomentowano, fizycznie dane są bardziej prawdopodobne w pamięci głównej niż w rejestrze danych, ale to nie usuwa problemu ze wskaźnikiem] . Pomyśl tylko o połączonej liście liczb całkowitych. Każdy element listy może być przechowywany w zupełnie innym miejscu, a wszystko, co utrzymuje listę razem, to wskaźniki od jednego elementu do drugiego. Gdybyś wziął te dane w obecnej postaci i próbował skopiować je na innym komputerze z tym samym programem, wystąpiłyby problemy:

  1. Przede wszystkim rejestr adresujący twoje dane przechowywane na jednym komputerze może być już wykorzystany do czegoś zupełnie innego na innym komputerze (ktoś przegląda wymianę stosów, a przeglądarka już zjadła całą tę pamięć). Więc jeśli po prostu zastąpisz te rejestry, pożegnaj się z przeglądarką. W związku z tym konieczne będzie ponowne rozmieszczenie wskaźników w strukturze, aby pasowały do ​​adresów, które masz wolne na drugim komputerze. Ten sam problem pojawia się podczas próby ponownego załadowania danych na tym samym komputerze w późniejszym czasie.
  2. Co się stanie, jeśli jakiś element zewnętrzny wskazuje na twoją strukturę lub twoja struktura ma wskaźniki na dane zewnętrzne, a ty nie przesłałeś? Segfault wszędzie! To stałoby się koszmarem debugowania.

Czytelność przez inny program

Załóżmy, że udało Ci się przydzielić odpowiednie adresy na innym komputerze, aby zmieściły się w nich dane. Jeśli dane są przetwarzane przez osobny program na tym komputerze (inny język), program ten może mieć zupełnie inne podstawowe rozumienie danych. Załóżmy, że masz obiekty C ++ ze wskaźnikami, ale Twój język docelowy nawet nie obsługuje wskaźników na tym poziomie. Ponownie, w drugim programie nie ma czystego sposobu na zajęcie się tymi danymi. Kończysz w pamięci jakieś dane binarne, ale musisz napisać dodatkowy kod, który otacza dane i w jakiś sposób tłumaczy go na coś, z czym twój język docelowy może współpracować. Brzmi jak deserializacja, tyle że punktem początkowym jest dziwny obiekt rozrzucony wokół głównej pamięci, który jest inny dla różnych języków źródłowych, zamiast pliku o dobrze zdefiniowanej strukturze. To samo oczywiście, jeśli spróbujesz bezpośrednio zinterpretować plik binarny zawierający wskaźniki - musisz napisać parsery dla każdego możliwego sposobu, w jaki inny język może reprezentować dane w pamięci.

Czytelność dla człowieka

Dwa z najbardziej znanych współczesnych języków serializacji do serializacji internetowej (xml, json) są łatwo zrozumiałe dla człowieka. Zamiast binarnego stosu mazi rzeczywista struktura i zawartość danych są jasne, nawet bez programu do ich odczytu. Ma to wiele zalet:

  • łatwiejsze debugowanie -> jeśli występuje problem w potoku usług, wystarczy spojrzeć na dane wychodzące z jednej usługi i sprawdzić, czy ma to sens (jako pierwszy krok); podczas pisania interfejsu eksportu bezpośrednio widzisz również, czy dane wyglądają tak, jak powinny.
  • możliwość archiwizacji: jeśli masz dane jako czysty plik binarny i tracisz program, który ma je zinterpretować, tracisz dane (lub będziesz musiał spędzić sporo czasu, aby coś tam znaleźć); jeśli twoje zserializowane dane są czytelne dla ludzi, możesz łatwo użyć ich jako archiwum lub zaprogramować własnego importera dla nowego programu
  • deklaratywny charakter danych zserializowanych w taki sposób oznacza również, że są one całkowicie niezależne od systemu komputerowego i jego sprzętu; możesz załadować go do zupełnie inaczej zbudowanego komputera kwantowego lub zainfekować obcą sztuczną inteligencję alternatywnymi faktami, aby przypadkowo poleciała na następne słońce (Emmerich, jeśli to przeczytasz, odniesienie byłoby fajne, jeśli wykorzystasz ten pomysł na następny 4 lipca film)

Moje dane są prawdopodobnie głównie w głównej pamięci, a nie w rejestrach. Jeśli moje dane mieszczą się w rejestrach, serializacja nie stanowi nawet problemu. Myślę, że źle zrozumiałeś, co to jest rejestr.
David Richerby

Rzeczywiście, użyłem tutaj zbyt luźno terminu rejestr. Ale najważniejsze jest to, że twoje dane mogą zawierać wskaźniki do przestrzeni adresowej, aby zidentyfikować własne komponenty lub odwołać się do innych danych. Nie ma znaczenia, czy jest to rejestr fizyczny, czy adres wirtualny w pamięci głównej.
Frank Hopkins

Nie, użyłeś terminu „zarejestruj się” całkowicie niepoprawnie. Rejestrowane przez ciebie rzeczy znajdują się w zupełnie innej części hierarchii pamięci niż rzeczywiste rejestry.
David Richerby

6

Oprócz tego, co mówią inne odpowiedzi:

Czasami chcesz serializować rzeczy, które nie są czystymi danymi.

Pomyśl na przykład o dojściu do pliku lub połączeniu z serwerem. Mimo że uchwyt pliku lub gniazdo to int, liczba ta nie ma znaczenia przy następnym uruchomieniu programu. Aby poprawnie odtworzyć obiekty zawierające uchwyty takich rzeczy, należy ponownie otworzyć pliki i odtworzyć połączenia oraz zdecydować, co zrobić, jeśli to się nie powiedzie.

Wiele języków obsługuje obecnie przechowywanie anonimowych funkcji w obiektach, na przykład onBlah()moduł obsługi w JavaScript. Jest to trudne, ponieważ taki kod może zawierać odniesienia do dodatkowych fragmentów danych, które z kolei wymagają serializacji. (A potem jest kwestia szeregowania kodu w sposób wieloplatformowy, co jest oczywiście łatwiejsze dla interpretowanych języków.) Mimo to, nawet jeśli obsługiwany jest tylko podzbiór języka, może okazać się całkiem użyteczny. Niewiele mechanizmów serializacji próbuje serializować kod, ale zobacz serialize-javascript .

W przypadkach, gdy chcesz serializować obiekt, ale zawiera on coś, co nie jest obsługiwane przez Twój mechanizm serializacji, musisz przepisać kod w sposób, który działa w ten sposób. Na przykład możesz użyć wyliczeń zamiast funkcji anonimowych, gdy istnieje ograniczona liczba możliwych funkcji.

Często chcesz, aby dane serializowane były zwięzłe.

Jeśli wysyłasz dane przez sieć lub nawet przechowujesz je na dysku, może być ważne, aby zachować mały rozmiar. Jednym z najprostszych sposobów osiągnięcia tego jest wyrzucenie informacji, które można odbudować (na przykład, odrzucając pamięci podręczne, tabele skrótów i alternatywne reprezentacje tych samych danych).

Oczywiście programista musi ręcznie wybrać, co ma zostać zapisane, a co wyrzucić, i upewnić się, że wszystko zostanie odbudowane po odtworzeniu obiektu.

Pomyśl o akcie zapisania gry. Obiekty mogą zawierać wiele wskaźników do danych graficznych, danych dźwiękowych i innych obiektów. Ale większość tych rzeczy można załadować z plików danych gry i nie trzeba ich przechowywać w pliku zapisu. Odrzucanie może być pracochłonne, więc często pozostawia się niewiele rzeczy. Sześciokrotnie edytowałem niektóre pliki składowania i odkryłem dane, które były wyraźnie zbędne, takie jak opisy tekstowe.

Czasami miejsce nie jest ważne, ale czytelność jest - w takim przypadku możesz zamiast tego użyć formatu ASCII (ewentualnie JSON lub XML).


3

Zdefiniujmy, czym właściwie jest sekwencja bajtów. Sekwencja bajtów składa się z nieujemnej liczby całkowitej zwanej długością i pewnej dowolnej funkcji / korespondencji, która odwzorowuje każdą liczbę całkowitą i, która jest co najmniej zero i mniejsza niż długość, na wartość bajtu (liczba całkowita od 0 do 255).

Wiele obiektów, z którymi masz do czynienia w typowym programie, nie ma tej postaci, ponieważ obiekty te składają się z wielu różnych przydziałów pamięci znajdujących się w różnych miejscach w pamięci RAM i mogą być oddzielone od siebie milionami bajtów rzeczy nie obchodzi mnie to. Pomyśl tylko o podstawowej połączonej liście: każdy węzeł na liście to sekwencja bajtów, tak, ale węzły znajdują się w wielu różnych lokalizacjach w pamięci twojego komputera i są połączone wskaźnikami. Albo po prostu pomyśl o prostej strukturze, która ma wskaźnik do łańcucha o zmiennej długości.

Powodem, dla którego chcemy serializować struktury danych w sekwencję bajtów, jest zazwyczaj to, że chcemy je zapisać na dysku lub wysłać do innego systemu (np. Przez sieć). Jeśli spróbujesz zapisać wskaźnik na dysku lub wysłać go do innego systemu, będzie to zupełnie bezużyteczne, ponieważ program odczytujący ten wskaźnik będzie miał inny zestaw dostępnych obszarów pamięci.


1
Nie jestem pewien, czy to świetna definicja sekwencji. Większość ludzi zdefiniowałaby sekwencję jako, cóż, sekwencję: linię rzeczy jedna po drugiej. Według twojej definicji int seq(int i) { if (0 <= i < length) return i+1; else return -1;}jest to sekwencja. Jak mam to zapisać na dysku?
David Richerby

1
Jeśli długość wynosi 4, przechowuję czterobajtowy plik z zawartością: 1, 2, 3, 4.
David Grayson

1
@DavidRicherby Jego definicja jest odpowiednikiem „linii rzeczy jedna po drugiej”, jest to po prostu bardziej matematyczna i precyzyjna definicja niż twoja intuicyjna definicja. Zauważ, że twoja funkcja nie jest sekwencją, ponieważ aby mieć sekwencję, potrzebujesz tej funkcji i innej liczby całkowitej, która nazywa się długością.
user253751

1
@FreshAir Chodzi mi o to, że sekwencja wynosi 1, 2, 3, 4, 5. To, co zapisałem, jest funkcją . Funkcja nie jest sekwencją.
David Richerby

1
Prostym sposobem zapisania funkcji na dysk jest ten, który już zaproponowałem: dla każdego możliwego wejścia zapisz dane wyjściowe. Myślę, że może nadal tego nie rozumiesz, ale nie jestem pewien, co powiedzieć. Czy wiesz, że w systemach wbudowanych konwersja drogich funkcji, takich jak sintablica odnośników, jest sekwencją liczb, jest powszechna ? Czy wiesz, że twoja funkcja jest taka sama jak dla danych, na których nam zależy? int seq(n) { int a[] = [1, 2, 3, 4]; return a[n]; } Dlaczego dokładnie twierdzisz, że mój czterobajtowy plik jest nieodpowiednią reprezentacją?
David Grayson

2

Zawiłości odzwierciedlają zawiłości samych danych i obiektów. Te obiekty mogą być obiektami ze świata rzeczywistego lub tylko komputerowymi. Odpowiedź jest w imieniu. Serializacja to liniowa reprezentacja obiektów wielowymiarowych. Istnieje wiele problemów innych niż rozdrobniona pamięć RAM.

Jeśli możesz spłaszczyć 12 pięciowymiarowych tablic i jakiś kod programu, serializacja pozwala również przenieść cały program komputerowy (i dane) między komputerami. Protokoły przetwarzania rozproszonego, takie jak RMI / CORBA, intensywnie wykorzystują serializację do przesyłania danych i programów.

Rozważ swój rachunek za telefon. Może to być pojedynczy obiekt, na który składają się wszystkie połączenia (lista ciągów), kwota do zapłaty (liczba całkowita) i kraj. Lub rachunek telefoniczny może być odwrócony od powyższego i składać się z dyskretnych wyszczególnionych połączeń telefonicznych powiązanych z Twoim nazwiskiem. Każdy spłaszczony będzie wyglądał inaczej, odzwierciedlając sposób, w jaki twoja firma telefoniczna napisała tę wersję swojego oprogramowania oraz powód, dla którego obiektowe bazy danych nigdy nie wystartowały.

Niektóre części struktury mogą nawet nie być w ogóle w pamięci. Jeśli masz opóźnione buforowanie, niektóre części obiektu mogą odnosić się tylko do pliku dyskowego i są ładowane tylko wtedy, gdy jest dostępna ta część tego konkretnego obiektu. Jest to powszechne w ramach poważnego uporczywości. BLOB są dobrym przykładem. Getty Images może przechowywać ogromne zdjęcie Fidela Castro o wielkości wielu megabajtów i niektóre metadane, takie jak nazwa obrazu, koszt wynajmu i sam obraz. Możesz nie chcieć ładować 200 MB obrazu za każdym razem, chyba że na niego spojrzysz. Serializowany cały plik wymagałby ponad 200 MB pamięci.

Niektórych obiektów w ogóle nie można nawet serializować. W krainie programowania Java możesz mieć obiekt programowy reprezentujący ekran graficzny lub fizyczny port szeregowy. Żadna z nich nie ma prawdziwej koncepcji szeregowania. Jak wysłałbyś swój port do kogoś innego przez sieć?

Niektóre rzeczy, takie jak hasła / klucze szyfrowania, nie powinny być przechowywane ani przesyłane. Mogą być oznaczone jako takie (niestabilne / przejściowe itp.), A proces serializacji pominie je, ale mogą żyć w pamięci RAM. Pominięcie tych tagów to sposób, w jaki klucze szyfrujące zostają przypadkowo wysłane / zapisane w zwykłym ASCII.

Ta i inne odpowiedzi sprawiają, że jest skomplikowana.


2

Problem, który mam, jest następujący: czy wszystkie zmienne (czy to prymitywy, takie jak int czy obiekty złożone) nie są już reprezentowane przez sekwencję bajtów?

Tak, oni są. Problemem jest tutaj układ tych bajtów. Prosta intmoże mieć długość 2, 4 lub 8 bitów. Może być duży lub mały endian. Może być niepodpisany, podpisany uzupełnieniem 1 lub nawet w jakimś bardzo egzotycznym kodowaniu bitów, takim jak negabinary.

Jeśli po prostu intzrzucisz plik binarny z pamięci i nazwiesz go „serializowanym”, musisz podłączyć prawie cały komputer, system operacyjny i program, aby można go było zdemrializować. A przynajmniej ich dokładny opis.

Co sprawia, że ​​serializacja jest tak głębokim tematem? Aby serializować zmienną, czy nie możemy po prostu pobrać tych bajtów do pamięci i zapisać je w pliku? Jakie zawiłości przeoczyłem?

Serializacja prostego obiektu polega na zapisywaniu go zgodnie z pewnymi zasadami. Te zasady są liczne i nie zawsze oczywiste. Np. xs:integerW XML jest napisany w bazie-10. Nie base-16, nie base-9, ale 10. To nie jest ukryte założenie, to rzeczywista zasada. I dzięki takim regułom serializacja staje się serializacją. Ponieważ właściwie nie ma reguł dotyczących układu bitów twojego programu w pamięci .

To był tylko wierzchołek góry lodowej. Weźmy przykład sekwencji tych najprostszych prymitywów: a C struct. Możesz tak myśleć

struct {
short width;
short height;
long count;
}

ma zdefiniowany układ pamięci na danym komputerze + system operacyjny? Tak nie jest. W zależności od bieżącego #pragma packustawienia kompilator wypełnia pola. Przy domyślnych ustawieniach kompilacji 32-bitowej oba shortszostaną uzupełnione do 4 bajtów, więc w structrzeczywistości będą miały 3 pola po 4 bajty. Więc teraz musisz nie tylko określić, że shortma on 16 bitów, jest to liczba całkowita, zapisana w uzupełnieniu 1 ujemnym, dużym lub małym endianem. Musisz także zapisać ustawienia pakowania struktury, z którymi skompilowany został Twój program.

Właśnie o to chodzi w serializacji: tworzenie zestawu reguł i trzymanie się ich.

Reguły te można następnie rozszerzyć, aby akceptowały nawet bardziej wyrafinowane struktury (takie jak listy o zmiennej długości lub dane nieliniowe), dodano funkcje takie jak czytelność, wersjonowanie, kompatybilność wsteczna i korekta błędów itp. Ale nawet zapisanie jednego intjest już wystarczająco skomplikowane, jeśli chcę tylko upewnić się, że będziesz w stanie rzetelnie go odczytać.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.