GoSuda

Go 1.25 encoding/json/v2 käyttäytymismuutokset ja v1-yhteensopivuuskerroksen analyysi

By lemonmint views ...

Go-kielen encoding/json-paketin v2 on uusi toteutus, jonka tavoitteena on parantaa vanhan v1:n useita heikkouksia (johdonmukaisuuden puute, yllättävä käyttäytyminen, suorituskykyongelmat). Se on kokeellinen ominaisuus, joka otetaan käyttöön goexperiment.jsonv2-koontilipun kautta.

Tärkeintä on, että kun v2 on käytössä, v1 toimii yhteensopivuuskerroksena, joka emuloi v1:n käyttäytymistä v2-toteutuksen päällä. Tämä toteutuu v2_v1.go-tiedoston DefaultOptionsV1()-funktion kautta. Toisin sanoen v2 tarjoaa asetukset, joilla v1:n käyttäytyminen voidaan toistaa täydellisesti, ja samalla se esittelee tiukemmat ja ennustettavammat uudet oletuskäyttäytymiset.

v2:n pääasialliset tavoitteet ovat seuraavat:

  1. Tarkkuuden ja ennustettavuuden parantaminen: Oletuksena sovelletaan tiukempia sääntöjä (esimerkiksi isot ja pienet kirjaimet erottava vertailu, kielletyt kaksoiskaviot), mikä vähentää odottamatonta käyttäytymistä.
  2. Suorituskyvyn parantaminen: Jäsennys- ja koodausmoottori on suunniteltu uudelleen tehokkuuden lisäämiseksi.
  3. Joustavuuden ja hallinnan laajentaminen: Yksityiskohtainen asetusjärjestelmä mahdollistaa kehittäjille JSON-käsittelyn hienojakoisen hallinnan.

Keskeiset merkitys- ja käyttäytymiserot

Erot v1:n ja v2:n välillä on koottu kohdittain, pääpainon ollessa v2_test.go-tiedostossa.

1. Kenttänimien vastaavuus (isot ja pienet kirjaimet)

  • v1-käyttäytyminen: Kun JSON-olion jäseniä unmarshalloidaan Go-rakenteen kenttiin, vastaavuus tehdään kirjainkoosta riippumatta (case-insensitive). Sekä "FirstName" että "firstname" kartoittuvat kenttään FirstName.
  • v2-käyttäytyminen: Oletuksena vastaavuus on kirjainkokoherkkä (case-sensitive), ja kenttään kartoittuu vain tarkasti täsmäävä nimi.
  • Muutoksen syy: Kirjainkoosta riippumaton vastaavuus voi aiheuttaa odottamatonta käyttäytymistä, ja se heikentää suorituskykyä käsiteltäessä kenttiä, jotka eivät täsmää. v2 valitsi oletukseksi selkeämmän ja ennustettavamman käyttäytymisen.
  • Liittyvät asetukset: v2:ssa voidaan käyttää json:"...,case:ignore"-tagiasetusta, jolla kirjainkoosta riippumaton vastaavuus otetaan eksplisiittisesti käyttöön kentittäin, tai json.MatchCaseInsensitiveNames(true)-asetusta, joka otetaan käyttöön globaalisti.

2. omitempty-tagiasetuksen merkityksen muutos

  • v1-käyttäytyminen: Kenttä jätetään pois Go-arvon "tyhjän tilan" perusteella. Tyhjä tila tarkoittaa tässä arvoja false, 0, nil-osoitinta tai -liitäntää sekä pituudeltaan nollaa olevaa taulukkoa, viipaletta, karttaa tai merkkijonoa.
  • v2-käyttäytyminen: Kenttä jätetään pois koodatun JSON-arvon "tyhjän tilan" perusteella. Eli jos arvo koodautuu muotoon null, "", {} tai [], kenttä jätetään pois.
  • Muutoksen syy: v1:n määritelmä on sidoksissa Go-tyyppijärjestelmään. v2 perustuu JSON-tyyppijärjestelmään ja tarjoaa siten johdonmukaisemman käyttäytymisen. Esimerkiksi v1:ssä bool-tyypin arvo false jätetään pois, mutta v2:ssa false on JSON-arvo, joka ei ole tyhjä, eikä sitä jätetä pois. v2:ssa on lisätty omitzero-asetus, jolla voidaan korvata v1:n omitempty-käyttäytyminen, joka koski arvoja 0 ja false.
  • Liittyvät asetukset: Jos halutaan v1:n kanssa identtinen käyttäytyminen, käytetään asetusta json.OmitEmptyWithLegacyDefinition(true).

3. string-tagiasetuksen käyttäytymisen muutos

  • v1-käyttäytyminen: Sovelletaan kenttiin, joiden tyyppi on numero, totuusarvo tai merkkijono. Arvo koodataan uudelleen JSON-merkkijonon sisälle (esimerkiksi int(42) -> "42"). Yhdistettyjen tyyppien (viipaleet, kartat jne.) sisäisiin arvoihin ei sovelleta asetusta rekursiivisesti.
  • v2-käyttäytyminen: Sovelletaan vain numerotyyppeihin, ja asetus on rekursiivinen. Eli myös esimerkiksi []int-viipaleen sisäiset numerot koodataan kaikki JSON-merkkijonoiksi.
  • Muutoksen syy: string-asetuksen pääasiallinen käyttötarkoitus on estää 64-bittisten kokonaislukujen tarkkuuden menetys esittämällä numerot merkkijonoina. v1:n käyttäytyminen oli rajoittunutta ja johdonmukaisuudeltaan puutteellista. v2 keskittyy tähän keskeiseen käyttötarkoitukseen ja laajentaa käyttäytymistä rekursiivisesti, jolloin asetuksesta tuli hyödyllisempi.
  • Liittyvät asetukset: v1:n käyttäytymistä voidaan jäljitellä asetuksella json.StringifyWithLegacySemantics(true).

4. nil-viipaleiden ja -karttojen marshalointi

  • v1-käyttäytyminen: nil-viipale ja nil-kartta marshalloidaan muotoon null.
  • v2-käyttäytyminen: Oletuksena nil-viipale marshalloidaan muotoon [] (tyhjä taulukko) ja nil-kartta muotoon {} (tyhjä olio).
  • Muutoksen syy: nil on Go-kielen toteutuksen yksityiskohta, eikä sen paljastaminen kielestä riippumattomassa JSON-muodossa ole toivottavaa. Tyhjiä kokoelmia esittävät [] ja {} ovat yleisempiä ilmaisuja.
  • Liittyvät asetukset: v2:ssa voidaan marshalloida v1:n tapaan null-arvoksi käyttämällä asetuksia json.FormatNilSliceAsNull(true) tai json.FormatNilMapAsNull(true).

5. Taulukon unmarshalointi

  • v1-käyttäytyminen: Kun JSON-taulukko unmarshalloidaan Go-taulukkoon ([N]T), virhettä ei synny, vaikka JSON-taulukon pituus poikkeaisi Go-taulukon pituudesta. Jos JSON-taulukko on lyhyempi, ylimääräinen tila täytetään nollla-arvoilla, ja jos se on pidempi, ylimenevä osa hylätään.
  • v2-käyttäytyminen: JSON-taulukon pituuden on täsmättävä tarkalleen Go-taulukon pituuteen. Muussa tapauksessa syntyy virhe.
  • Muutoksen syy: Go-kielessä kiinteäkokoisen taulukon pituudella on usein merkittävä merkitys. v1:n käyttäytyminen voi aiheuttaa datan äänetöntä katoamista. v2 nostaa tarkkuutta tiukemmalla säännöllä.
  • Liittyvät asetukset: v1:n käyttäytymistä voidaan jäljitellä asetuksella json.UnmarshalArrayFromAnyLength(true).

6. time.Duration-tyypin käsittely

  • v1-käyttäytyminen: time.Duration käsitellään sisäisesti int64-tyyppinä, ja se koodataan nanosekuntiyksikössä olevaksi JSON-numeroksi.
  • v2-käyttäytyminen: Koodaus tehdään time.Duration.String()-metodin avulla JSON-merkkijonona muodossa, kuten "1h2m3s".
  • Muutoksen syy: Nanosekuntimuotoiset numerot ovat vaikeasti luettavia, ja time.Duration-tyypin vakiomerkkijonoesitys on hyödyllisempi.
  • Liittyvät asetukset: v1:n käyttäytymisen voi ottaa käyttöön tagiasetuksella json:",format:nano" tai asetuksella json.FormatTimeWithLegacySemantics(true).

7. Virheellisen UTF-8:n käsittely

  • v1-käyttäytyminen: Marshalloinnin ja unmarshaloinnin aikana merkkijonossa olevat virheelliset UTF-8-tavut korvataan äänettömästi Unicode-korvaavalla merkillä (\uFFFD).
  • v2-käyttäytyminen: Oletuksena virheellinen UTF-8 aiheuttaa virheen.
  • Muutoksen syy: Tarkoituksena on estää datan äänetön vioittuminen ja noudattaa tiukempaa JSON-standardia (RFC 7493).
  • Liittyvät asetukset: v1:n käyttäytymistä voidaan jäljitellä asetuksella jsontext.AllowInvalidUTF8(true).

8. Kaksoiskappaleiden käsittely objektin jäsennimissä

  • v1-käyttäytyminen: JSON-oliossa sallitaan saman nimisten jäsenten esiintyminen useaan kertaan. Viimeisenä esiintyvä arvo ylikirjoittaa aiemmat.
  • v2-käyttäytyminen: Oletuksena kaksoiskappaleena esiintyvä jäsennimi aiheuttaa virheen.
  • Muutoksen syy: RFC 8259 -standardi ei määrittele kaksoiskappaleiden käsittelyä, joten eri toteutukset voivat toimia eri tavoin. Tämä voi olla tietoturvahaavoittuvuuden lähde. v2 hylkää ne eksplisiittisesti, mikä parantaa tarkkuutta ja tietoturvaa.
  • Liittyvät asetukset: v1:n käyttäytymistä voidaan jäljitellä asetuksella jsontext.AllowDuplicateNames(true).

Toteutus- ja arkkitehtuurierot

  • v1: Luottaa voimakkaasti decode.go-tiedoston decodeState-rakenteeseen ja scanner.go-tiedoston käsin kirjoitettuun tilakoneeseen. Tämä on jäsennyslogiikan ja semanttisen analyysin tiukasti yhdistävä monoliittinen rakenne.
  • v2: Arkkitehtuuri on modulaarisempi.
    • encoding/json/jsontext: Tarjoaa matalan tason, suorituskykyisen JSON-tokenisoijan (Decoder) ja kooderin (Encoder). Tämä paketti keskittyy ainoastaan JSON-syntaksin puoleen.
    • encoding/json/v2: Käsittelee jsontext-paketin pohjalta Go-tyyppien ja JSON-arvojen välisen semanttisen muunnoksen.
    • Tämän jaottelun ansiosta syntaksianalyysi ja semanttinen analyysi on erotettu toisistaan, mikä on parantanut koodin selkeyttä ja suorituskykyä.

v2:n uudet API:t ja ominaisuudet

v2 tarjoaa erittäin joustavia hallintaominaisuuksia json.Options-asetusjärjestelmän kautta.

  • json.Options: Joukko asetuksia, jotka muuttavat marshalointi- ja unmarshalointikäyttäytymistä.
  • json.JoinOptions(...): Yhdistää useita asetuksia yhdeksi.
  • WithMarshalers / WithUnmarshalers: Tehokas ominaisuus, jolla serialisointi- ja deserialisointilogiikka voidaan lisätä tietylle tyypille ilman, että tyyppi toteuttaa Marshaler- tai Unmarshaler-rajapintaa. Tämä on erityisen hyödyllistä, kun käsitellään ulkoisten pakettien tyyppejä.
  • Uudet asetukset: RejectUnknownMembers, Deterministic(false), FormatNilSliceAsNull ja muut mahdollistavat käyttäytymisen hallinnan tavalla, joka ei ollut mahdollinen v1:ssä.

Yhteenveto

encoding/json v2 on nykyaikainen toteutus, joka perustuu v1:n kokemuksiin ja parantaa merkittävästi tarkkuutta, suorituskykyä ja joustavuutta. Oletuskäyttäytyminen on tiukempi, mutta hienostuneen Options-järjestelmän ansiosta v1:n kaikki käyttäytymiset voidaan toteuttaa täysin, joten olemassa oleva koodi säilyy yhteensopivana ja v2:n etuja voidaan ottaa käyttöön vaiheittain.

  • Uusi projekti kannattaa toteuttaa oletuksena v2:lla.
  • Olemassa olevat projektit voivat joko käyttää jsonv1-pakettia sellaisenaan tai siirtyä jsonv2-pakettiin ja ottaa v2:n tiukemman käyttäytymisen vaiheittain käyttöön DefaultOptionsV1()-funktion avulla.