Meer Jackson-aantekeningen

1. Overzicht

Dit artikel behandelt enkele aanvullende annotaties die niet werden behandeld in het vorige artikel, A Guide to Jackson Annotations - we zullen er zeven doornemen.

2. @JsonIdentityReference

@JsonIdentityReference wordt gebruikt voor het aanpassen van verwijzingen naar objecten die worden geserialiseerd als objectidentiteiten in plaats van volledige POJO's. Het werkt samen met @JsonIdentityInfo om het gebruik van objectidentiteiten in elke serialisatie af te dwingen, anders dan alles behalve de eerste keer wanneer @JsonIdentityReference is afwezig. Deze paar annotaties zijn het nuttigst bij het omgaan met cirkelvormige afhankelijkheden tussen objecten. Raadpleeg sectie 4 van het artikel Jackson - Bidirectional Relationship voor meer informatie.

Om het gebruik aan te tonen @JsonIdentityReference, zullen we twee verschillende bean-klassen definiëren, zonder en met deze annotatie.

De boon zonder @JsonIdentityReference:

@JsonIdentityInfo (generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") openbare klasse BeanWithoutIdentityReference {privé int id; private String naam; // constructeur, getters en setters}

Voor het gebruik van de boon @JsonIdentityReference, we kiezen voor de ID kaart eigenschap om de objectidentiteit te zijn:

@JsonIdentityInfo (generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") @JsonIdentityReference (alwaysAsId = true) openbare klasse BeanWithIdentityReference {privé int id; private String naam; // constructeur, getters en setters}

In het eerste geval, waar @JsonIdentityReference afwezig is, wordt die boon geserialiseerd met volledige details over zijn eigenschappen:

BeanWithoutIdentityReference bean = new BeanWithoutIdentityReference (1, "Bean Without Identity Reference Annotation"); String jsonString = mapper.writeValueAsString (bean);

De output van de bovenstaande serialisatie:

{"id": 1, "name": "Bean Without Identity Reference Annotation"}

Wanneer @JsonIdentityReference wordt gebruikt, wordt de boon in plaats daarvan geserialiseerd als een eenvoudige identiteit:

BeanWithIdentityReference bean = new BeanWithIdentityReference (1, "Bean With Identity Reference Annotation"); String jsonString = mapper.writeValueAsString (bean); assertEquals ("1", jsonString);

3. @JsonAppend

De @JsonAppend annotatie wordt gebruikt om virtuele eigenschappen aan een object toe te voegen naast gewone eigenschappen wanneer dat object is geserialiseerd. Dit is nodig als we aanvullende informatie rechtstreeks aan een JSON-string willen toevoegen in plaats van de klassedefinitie te wijzigen. Het kan bijvoorbeeld handiger zijn om de versie metadata van een bean naar het corresponderende JSON-document dan om het van een extra eigenschap te voorzien.

Stel dat we een boon hebben zonder @JsonAppend als volgt:

openbare klasse BeanWithoutAppend {privé int id; private String naam; // constructeur, getters en setters}

Een test zal bevestigen dat bij afwezigheid van de @JsonAppend annotatie, bevat de serialisatie-uitvoer geen informatie over de aanvullende versie eigenschap, ondanks het feit dat we proberen toe te voegen aan de ObjectWriter voorwerp:

BeanWithoutAppend bean = new BeanWithoutAppend (2, "Bean Without Append Annotation"); ObjectWriter writer = mapper.writerFor (BeanWithoutAppend.class) .withAttribute ("versie", "1.0"); String jsonString = writer.writeValueAsString (bean);

De serialisatie-output:

{"id": 2, "name": "Bean Without Append Annotation"}

Laten we nu zeggen dat we een boon hebben met de annotatie @JsonAppend:

@JsonAppend (attrs = {@ JsonAppend.Attr (value = "version")}) openbare klasse BeanWithAppend {privé int id; private String naam; // constructeur, getters en setters}

Een vergelijkbare test als de vorige zal verifiëren dat wanneer het @JsonAppend annotatie wordt toegepast, de aanvullende eigenschap wordt opgenomen na serialisering:

BeanWithAppend bean = new BeanWithAppend (2, "Bean With Append Annotation"); ObjectWriter writer = mapper.writerFor (BeanWithAppend.class) .withAttribute ("versie", "1.0"); String jsonString = writer.writeValueAsString (bean);

De output van die serialisatie laat zien dat de versie eigenschap is toegevoegd:

{"id": 2, "name": "Bean With Append Annotation", "version": "1.0"}

4. @JsonNaming

De @JsonNaming annotatie wordt gebruikt om de naamgevingsstrategieën voor eigenschappen in serialisering te kiezen, waarbij de standaard wordt overschreven. De ... gebruiken waarde element, kunnen we elke strategie specificeren, inclusief aangepaste.

Naast de standaard, dat is LOWER_CAMEL_CASE (bijv. lowerCamelCase), Voorziet de Jackson-bibliotheek ons ​​voor het gemak van vier andere ingebouwde naamgevingsstrategieën voor eigendommen:

  • KEBAB_CASE: Naamelementen worden gescheiden door koppeltekens, bijv. kebab-zaak.
  • LOWER_CASE: Alle letters zijn kleine letters zonder scheidingstekens, bijv. kleine letters.
  • SNAKE_CASE: Alle letters zijn kleine letters met onderstrepingstekens als scheidingstekens tussen naamelementen, bijv. snake_case.
  • UPPER_CAMEL_CASE: Alle naamelementen, inclusief de eerste, beginnen met een hoofdletter, gevolgd door kleine letters en er zijn geen scheidingstekens, bijv. UpperCamelCase.

Dit voorbeeld illustreert de manier om eigenschappen te serialiseren met behulp van snake case-namen, waarbij een eigenschap met de naam beanName is geserialiseerd als bean_name.

Gegeven een boondefinitie:

@JsonNaming (PropertyNamingStrategy.SnakeCaseStrategy.class) openbare klasse NamingBean {privé int id; private String beanName; // constructeur, getters en setters}

De onderstaande test laat zien dat de opgegeven naamgevingsregel werkt zoals vereist:

NamingBean bean = nieuwe NamingBean (3, "Naming Bean"); String jsonString = mapper.writeValueAsString (bean); assertThat (jsonString, bevatString ("bean_name"));

De jsonString variabele bevat de volgende gegevens:

{"id": 3, "bean_name": "Bean benoemen"}

5. @JsonPropertyDescription

De Jackson-bibliotheek is in staat om JSON-schema's voor Java-typen te maken met behulp van een afzonderlijke module genaamd JSON Schema. Het schema is handig als we de verwachte uitvoer willen specificeren bij het serialiseren van Java-objecten, of om een ​​JSON-document te valideren vóór deserialisatie.

De @JsonPropertyDescription annotatie maakt het mogelijk een voor mensen leesbare beschrijving toe te voegen aan het gemaakte JSON-schema door de Omschrijving veld.

Deze sectie maakt gebruik van de hieronder aangegeven boon om de mogelijkheden van te demonstreren @JsonPropertyDescription:

openbare klasse PropertyDescriptionBean {privé int id; @JsonPropertyDescription ("Dit is een beschrijving van de eigenschap name") private String naam; // getters en setters}

De methode voor het genereren van een JSON-schema met de toevoeging van de Omschrijving veld wordt hieronder getoond:

SchemaFactoryWrapper wrapper = nieuwe SchemaFactoryWrapper (); mapper.acceptJsonFormatVisitor (PropertyDescriptionBean.class, wrapper); JsonSchema jsonSchema = wrapper.finalSchema (); String jsonString = mapper.writeValueAsString (jsonSchema); assertThat (jsonString, containsString ("Dit is een beschrijving van de eigenschap name"));

Zoals we kunnen zien, was het genereren van het JSON-schema succesvol:

{"type": "object", "id": "urn: jsonschema: com: baeldung: jackson: annotation: extra: PropertyDescriptionBean", "properties": {"name": {"type": "string", " description ":" Dit is een beschrijving van de naam eigenschap "}," id ": {" type ":" integer "}}}

6. @BuienRadarNL

De @BuienRadarNL annotatie wordt gebruikt om een ​​builder-klasse te configureren om deserialisatie van een JSON-document aan te passen om POJO's te herstellen wanneer de naamgevingsconventie verschilt van de standaard.

Stel dat we de volgende JSON-string moeten deserialiseren:

{"id": 5, "name": "POJO Builder Bean"}

Die JSON-bron wordt gebruikt om een ​​instantie van het POJOBuilderBean:

@JsonDeserialize (builder = BeanBuilder.class) openbare klasse POJOBuilderBean {privé int identiteit; private String beanName; // constructeur, getters en setters}

De namen van de eigenschappen van de boon verschillen van die van de velden in de JSON-string. Dit is waar @BuienRadarNL komt te hulp.

De @BuienRadarNL annotatie gaat vergezeld van twee eigenschappen:

  • buildMethodName: De naam van de no-arg-methode die wordt gebruikt om de verwachte bean te instantiëren na het binden van JSON-velden aan de eigenschappen van die bean. De standaardnaam is bouwen.
  • metPrefix: Het naamvoorvoegsel voor automatische detectie van overeenkomsten tussen de eigenschappen van JSON en bonen. Het standaardvoorvoegsel is met.

Dit voorbeeld maakt gebruik van de BeanBuilder klasse hieronder, die wordt gebruikt op POJOBuilderBean:

@JsonPOJOBuilder (buildMethodName = "createBean", withPrefix = "construct") openbare klasse BeanBuilder {privé int idValue; private String nameValue; openbare BeanBuilder constructId (int id) {idValue = id; dit teruggeven; } openbare BeanBuilder constructName (String naam) {nameValue = naam; dit teruggeven; } openbare POJOBuilderBean createBean () {retourneer nieuwe POJOBuilderBean (idValue, nameValue); }}

In de bovenstaande code hebben we de @BuienRadarNL om een ​​build-methode genaamd createBean en de construeren prefix voor overeenkomende eigenschappen.

De toepassing van @BuienRadarNL aan een boon wordt als volgt beschreven en getest:

String jsonString = "{\" id \ ": 5, \" naam \ ": \" POJO Builder Bean \ "}"; POJOBuilderBean bean = mapper.readValue (jsonString, POJOBuilderBean.class); assertEquals (5, bean.getIdentity ()); assertEquals ("POJO Builder Bean", bean.getBeanName ());

Het resultaat laat zien dat een nieuw gegevensobject met succes opnieuw is gemaakt vanuit een JSON-bron in ondanks een niet-overeenkomende naam van de eigenschappen.

7. @JsonTypeId

De @JsonTypeId annotatie wordt gebruikt om aan te geven dat de geannoteerde eigenschap moet worden geserialiseerd als het type id wanneer informatie over polymorfe typen wordt opgenomen, in plaats van als een reguliere eigenschap. Die polymorfe metadata worden gebruikt tijdens deserialisatie om objecten van dezelfde subtypen opnieuw te creëren als vóór de serialisatie, in plaats van de gedeclareerde supertypes.

Zie sectie 2 van de Inheritance in Jackson voor meer informatie over de manier waarop Jackson met overerving omgaat.

Laten we zeggen dat we als volgt een bonenklasse-definitie hebben:

openbare klasse TypeIdBean {privé int id; @JsonTypeId private String-naam; // constructeur, getters en setters}

De volgende test bevestigt dat @JsonTypeId werkt zoals het bedoeld is om:

mapper.enableDefaultTyping (DefaultTyping.NON_FINAL); TypeIdBean bean = nieuwe TypeIdBean (6, "Type Id Bean"); String jsonString = mapper.writeValueAsString (bean); assertThat (jsonString, containsString ("Type Id Bean"));

De output van het serialisatieproces:

["Type Id Bean", {"id": 6}]

8. @JsonTypeIdResolver

De @JsonTypeIdResolver annotatie wordt gebruikt om een ​​aangepaste identiteitshandler aan te duiden bij serialisatie en deserialisatie. Die handler is verantwoordelijk voor de conversie tussen Java-typen en type-id die zijn opgenomen in een JSON-document.

Stel dat we type-informatie in een JSON-string willen insluiten bij het omgaan met de volgende klassenhiërarchie.

De SamenvattingBean superklasse:

@JsonTypeInfo (gebruik = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "@type") @JsonTypeIdResolver (BeanIdResolver.class) openbare klasse AbstractBean {privé int id; beschermde AbstractBean (int id) {this.id = id; } // no-arg constructor, getter en setter}

De FirstBean subklasse:

openbare klasse FirstBean breidt AbstractBean {String firstName; openbare FirstBean (int id, String naam) {super (id); setFirstName (naam); } // no-arg constructor, getter en setter}

De LastBean subklasse:

openbare klasse LastBean breidt AbstractBean {String lastName; openbare LastBean (int id, String naam) {super (id); setLastName (naam); } // no-arg constructor, getter en setter}

Instanties van die klassen worden gebruikt om een BeanContainer voorwerp:

openbare klasse BeanContainer {bonen van de privélijst; // getter en setter}

We kunnen zien dat de SamenvattingBean klasse is geannoteerd met @JsonTypeIdResolver, wat aangeeft dat het een aangepast TypeIdResolver om te beslissen hoe u subtype-informatie in serialisatie opneemt en hoe u die metadata andersom gebruikt.

Hier is de resolver-klasse om het opnemen van type-informatie af te handelen:

openbare klasse BeanIdResolver breidt TypeIdResolverBase {privé JavaType superType uit; @Override public void init (JavaType baseType) {superType = baseType; } @Override openbare id getMechanism () {retourneer id NAAM; } @Override public String idFromValue (Object obj) {return idFromValueAndType (obj, obj.getClass ()); } @Override public String idFromValueAndType (Object obj, Class subType) {String typeId = null; switch (subType.getSimpleName ()) {case "FirstBean": typeId = "bean1"; breken; case "LastBean": typeId = "bean2"; } return typeId; } @Override openbaar JavaType typeFromId (DatabindContext-context, String-id) {Class subType = null; switch (id) {case "bean1": subType = FirstBean.class; breken; case "bean2": subType = LastBean.class; } retourneer context.constructSpecializedType (superType, subType); }}

De twee meest opvallende methoden zijn idFromValueAndType en typeFromId, waarbij de eerste de manier aangeeft om type-informatie op te nemen bij het serialiseren van POJO's en de laatste de subtypen bepaalt van opnieuw gemaakte objecten met behulp van die metadata.

Om er zeker van te zijn dat zowel serialisatie als deserialisatie goed werken, laten we een test schrijven om de volledige voortgang te valideren.

Eerst moeten we een bonencontainer en bonenklassen instantiëren, en vervolgens die container vullen met boneninstanties:

FirstBean bean1 = nieuwe FirstBean (1, "Bean 1"); LastBean bean2 = nieuwe LastBean (2, "Bean 2"); Lijstbonen = nieuwe ArrayList (); bonen.add (bean1); bonen.add (bean2); BeanContainer serializedContainer = nieuwe BeanContainer (); serializedContainer.setBeans (bonen);

Vervolgens de BeanContainer object is geserialiseerd en we bevestigen dat de resulterende string type-informatie bevat:

String jsonString = mapper.writeValueAsString (serializedContainer); assertThat (jsonString, bevatString ("bean1")); assertThat (jsonString, bevatString ("bean2"));

De output van serialisatie wordt hieronder weergegeven:

{"bonen": [{"@type": "bean1", "id": 1, "firstName": "Bean 1"}, {"@type": "bean2", "id": 2, "lastName ":" Bean 2 "}]}

Die JSON-structuur zal worden gebruikt om objecten van dezelfde subtypen opnieuw te maken als vóór serialisering. Hier zijn de implementatiestappen voor deserialisatie:

BeanContainer deserializedContainer = mapper.readValue (jsonString, BeanContainer.class); Lijst beanList = deserializedContainer.getBeans (); assertThat (beanList.get (0), instanceOf (FirstBean.class)); assertThat (beanList.get (1), instanceOf (LastBean.class));

9. Conclusie

Deze tutorial heeft een aantal minder voorkomende Jackson-annotaties in detail uitgelegd. De implementatie van deze voorbeelden en codefragmenten is te vinden in een GitHub-project.