3
votes

Comment faire une désérialisation partielle avec Jackson

Dans mon microservice REST, je demande un objet à un autre service qui me renvoie un énorme JSON imbriqué, par exemple:

public class FullAddress {
  String address;
  RawValue contactInfo;
}

Ce dont j'ai besoin pour récupérer ces données à partir d'un autre service, effectuer une modification uniquement dans le premier champ, puis les envoyer via HTTP. Ce que j'essaie d'éviter, c'est de désérialiser tout de mon côté et d'avoir à maintenir les classes ici. Existe-t-il un moyen d'obtenir la valeur brute de contact_info et d'avoir juste la représentation de l'adresse avec Jackson? Quelque chose comme ça:

{
  "address": "19th Street",
  "contact_info": {
     "phones": {
        "home": {
           "first": {
             "another_nested": 1234
           }
        }
     } 
  } 
}


2 commentaires

vérifiez ce newtonsoft.com/json/help/html/SerializingJSONFragments.htm


J'apprécie tout commentaire sur ma réponse .


5 Réponses :


2
votes

Vous pouvez utiliser JsonIgnoreProperties sur les documents de classe afin qu'il ignore toutes les autres propriétés qui ne sont pas présentes dans la classe

// To ignore any unknown properties in JSON input without exception:
@JsonIgnoreProperties(ignoreUnknown=true)


2 commentaires

Je pense que l'utilisateur souhaite désérialiser les données, les modifier et les remettre en série dans json. Avec @JsonIgnoreProperties vous perdez des données qui ont été ignorées.


Je pense que vous avez manqué le point de la question. J'ai fourni quelques solutions ma réponse .



7
votes

L'approche la plus simple consisterait à utiliser JsonNode :

@Data
public class FullAddress {

    private String address;

    @JsonDeserialize(using = RawJsonDeserializer.class)
    private String contactInfo;
}

Ou soit Map<String, Object> :

public class RawJsonDeserializer extends JsonDeserializer<String> {

    @Override
    public String deserialize(JsonParser jp, DeserializationContext ctxt)
           throws IOException, JsonProcessingException {

        ObjectMapper mapper = (ObjectMapper) jp.getCodec();
        JsonNode node = mapper.readTree(jp);
        return mapper.writeValueAsString(node);
    }
}

Cela fonctionne à la fois pour la sérialisation et la désérialisation.


Cependant, si vous souhaitez stocker le JSON brut, vous pouvez définir un désérialiseur personnalisé:

@Data
public class FullAddress {
    private String address;
    private Map<String, Object> contactInfo;
}

Et puis utilisez-le comme suit:

@Data
public class FullAddress {
    private String address;
    private JsonNode contactInfo;
}

Pour sérialisation retour, cependant, vous pouvez annoter le contactInfo champ avec @JsonRawValue .


2 commentaires

Fonctionne comme un charme!


cela ne fonctionne pas lors de l'utilisation de xml, car Jackson conservera une représentation JSON interne du XML.



0
votes

Il existe également ObjectMapper.readerForUpdating qui vous permet essentiellement de mettre à jour sur place un objet JSON. Cependant, il n'y a pas beaucoup de documentation à ce sujet:

https://fasterxml.github.io/jackson-databind/javadoc/2.9/com/fasterxml/jackson/databind/ObjectMapper.html#readerForUpdating-java.lang.Object-

Exemple ici: https://www.logicbig.com/tutorials/misc/jackson/reader-for-updating.html

(Toutes mes excuses pour une réponse partielle ici - mais cela pourrait être utile pour quelqu'un)


0 commentaires

0
votes

Je recommande de toujours définir des classes de modèle pour votre chaîne JSON, même si vous ne devez mapper que quelques champs de cette chaîne JSON. C'est plus soigné et à l'avenir, n'importe qui peut facilement ajouter des champs supplémentaires au modèle si cela est nécessaire. Jackson prend en charge le mappage partiel.

Exemple de travail complet à Kotlin:

import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import org.junit.Test

class Test {

    data class ModelInner(
            var attributeB1: String? = null,
    )

    data class ModelRoot(
            var attributeA2: List<ModelInner>? = null,
    )

    @Test
    fun test0() {
        val json = """
        {
            "attributeA0": "A0",
            "attributeA1": "A1",
            "attributeA2": [
                {
                    "attributeB0": "B00",
                    "attributeB1": "B01"
                },
                {
                    "attributeB0": "B10",
                    "attributeB1": "B11"
                }
            ]
        }
        """

        val om = ObjectMapper()
        om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

        val order = om.readValue(json, ModelRoot::class.java)

        print(order)
    }
}

Production:

ModelRoot (attributA2 = [ModelInner (attributB1 = B01), ModelInner (attributB1 = B11)])


0 commentaires

0
votes

Une autre solution consiste à utiliser @JsonAnyGetter @JsonAnySetter . Voir la rédaction ici: https://www.concretepage.com/jackson-api/jackson-jsonanygetter-and-jsonanysetter-example


0 commentaires