web-dev-qa-db-fra.com

Bonne façon d'écrire le désérialiseur JSON au printemps ou de l'étendre

J'essaie d'écrire un désérialiseur JSON personnalisé au printemps. Je souhaite utiliser le sérialiseur par défaut pour la plupart des champs et utiliser un désérialiseur personnalisé pour quelques propriétés. C'est possible? J'essaie de cette façon parce que, la plupart des propriétés sont des valeurs, je peux donc laisser Jackson utiliser le désérialiseur par défaut; mais peu de propriétés sont des références, donc dans le désérialiseur personnalisé, je dois interroger une base de données pour obtenir le nom de référence et obtenir la valeur de référence de la base de données.

Je montrerai du code si nécessaire.

51
gc5

J'ai beaucoup cherché et le meilleur moyen que j'ai trouvé jusqu'à présent est sur ce article :

Classe à sérialiser

package net.sghill.example;

import net.sghill.example.UserDeserializer
import net.sghill.example.UserSerializer
import org.codehaus.jackson.map.annotate.JsonDeserialize;
import org.codehaus.jackson.map.annotate.JsonSerialize;

@JsonDeserialize(using = UserDeserializer.class)
public class User {
    private ObjectId id;
    private String   username;
    private String   password;

    public User(ObjectId id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }

    public ObjectId getId()       { return id; }
    public String   getUsername() { return username; }
    public String   getPassword() { return password; }
}

Classe de désérialiseur

package net.sghill.example;

import net.sghill.example.User;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.ObjectCodec;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.JsonDeserializer;

import Java.io.IOException;

public class UserDeserializer extends JsonDeserializer<User> {

    @Override
    public User deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        ObjectCodec oc = jsonParser.getCodec();
        JsonNode node = oc.readTree(jsonParser);
        return new User(null, node.get("username").getTextValue(), node.get("password").getTextValue());
    }
}

Edit: Vous pouvez également consulter cet article , qui utilise les nouvelles versions de com.fasterxml.jackson.databind.JsonDeserializer.

78
gc5

J'essayais de @Autowire Un service géré par Spring dans mon Deserializer. Quelqu'un m'a prévenu de Jackson en utilisant l'opérateur new lors de l'appel des sérialiseurs/désérialiseurs. Cela signifiait pas d'auto-câblage de l'instance de Jackson de mon Deserializer. Voici comment j'ai pu @Autowire Ma classe de service dans mon Deserializer:

context.xml

<mvc:annotation-driven>
  <mvc:message-converters>
    <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
      <property name="objectMapper" ref="objectMapper" />
    </bean>
  </mvc:message-converters>
</mvc>
<bean id="objectMapper" class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
    <!-- Add deserializers that require autowiring -->
    <property name="deserializersByType">
        <map key-type="Java.lang.Class">
            <entry key="com.acme.Anchor">
                <bean class="com.acme.AnchorDeserializer" />
            </entry>
        </map>
    </property>
</bean>

Maintenant que mon Deserializer est un bean géré par Spring, le câblage automatique fonctionne!

AnchorDeserializer.Java

public class AnchorDeserializer extends JsonDeserializer<Anchor> {
    @Autowired
    private AnchorService anchorService;
    public Anchor deserialize(JsonParser parser, DeserializationContext context)
             throws IOException, JsonProcessingException {
        // Do stuff
    }
}

AnchorService.Java

@Service
public class AnchorService {}

Mise à jour: Bien que ma réponse initiale ait fonctionné pour moi lorsque j'ai écrit ceci, la réponse de @ xi.lin correspond exactement à ce qui est nécessaire. Belle trouvaille!

9
Beez

Avec Spring MVC 4.2.1.RELEASE, vous devez utiliser les nouvelles dépendances de Jackson2 comme ci-dessous pour que le désérialiseur fonctionne.

Ne pas utiliser cette

<dependency>  
            <groupId>org.codehaus.jackson</groupId>  
            <artifactId>jackson-mapper-asl</artifactId>  
            <version>1.9.12</version>  
        </dependency>  

Utilisez ceci à la place.

<dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.2.2</version>
        </dependency>  

Utiliser aussi com.fasterxml.jackson.databind.JsonDeserializer et com.fasterxml.jackson.databind.annotation.JsonDeserialize pour la désérialisation et non les classes de org.codehaus.jackson

1
VimalKumar