web-dev-qa-db-fra.com

Jackson peut-il être configuré pour supprimer les espaces de début / fin de toutes les propriétés de chaîne?

Exemple JSON (notez que la chaîne a des espaces de fin):

{ "aNumber": 0, "aString": "string   " }

Idéalement, l'instance désérialisée aurait une propriété aString avec une valeur de "string" (c'est-à-dire sans espaces de fin). Cela semble être quelque chose qui est probablement pris en charge mais je ne le trouve pas (par exemple dans DeserializationConfig.Feature).

Nous utilisons Spring MVC 3.x, donc une solution basée sur Spring conviendrait également.

J'ai essayé de configurer WebDataBinder de Spring sur la base d'une suggestion dans un post sur le forum mais cela ne semble pas fonctionner lors de l'utilisation d'un convertisseur de message Jackson:

@InitBinder
public void initBinder( WebDataBinder binder )
{
    binder.registerCustomEditor( String.class, new StringTrimmerEditor( " \t\r\n\f", true ) );
}
39
penfold

Avec un désérialiseur personnalisé , vous pouvez effectuer les opérations suivantes:

 <your bean>
 @JsonDeserialize(using=WhiteSpaceRemovalSerializer.class)
 public void setAString(String aString) {
    // body
 }

 <somewhere>
 public class WhiteSpaceRemovalDeserializer extends JsonDeserializer<String> {
     @Override
     public String deserialize(JsonParser jp, DeserializationContext ctxt) {
         // This is where you can deserialize your value the way you want.
         // Don't know if the following expression is correct, this is just an idea.
         return jp.getCurrentToken().asText().trim();
     }
 }

Cette solution implique que cet attribut de bean sera toujours sérialisé de cette façon, et vous devrez annoter chaque attribut que vous souhaitez désérialiser de cette façon.

18
DCKing

Solution facile pour les utilisateurs de Spring Boot, ajoutez simplement l'extension SimpleModule de walv à votre contexte d'application:

package com.example;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.stereotype.Component;

import Java.io.IOException;

@Component
public class StringTrimModule extends SimpleModule {

    public StringTrimModule() {
        addDeserializer(String.class, new StdScalarDeserializer<String>(String.class) {
            @Override
            public String deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException,
                    JsonProcessingException {
                return jsonParser.getValueAsString().trim();
            }
        });
    }
}

Une autre façon de personnaliser Jackson consiste à ajouter des beans de type com.fasterxml.jackson.databind.Module à votre contexte. Ils seront enregistrés avec chaque bean de type ObjectMapper, fournissant un mécanisme global de contribution de modules personnalisés lorsque vous ajoutez de nouvelles fonctionnalités à votre application.

http://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html#howto-customize-the-jackson-objectmapper

si vous n'utilisez pas Spring Boot, vous devez enregistrer le StringTrimModule vous-même (vous n'avez pas besoin de l'annoter avec @Component)

<bean class="org.springframework.http.converter.json.Jackson2Objec‌​tMapperFactoryBean">
    <property name="modulesToInstall" value="com.example.StringTrimModule"/>
</bean
22
Maciej Marczuk

Le problème de l'annotation @JsonDeserialize est que vous devez toujours vous rappeler de la mettre sur le setter. Pour le faire globalement "une fois pour toutes" avec Spring MVC, j'ai fait les étapes suivantes:

pom.xml:

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

Créez un ObjectMapper personnalisé:

package com.mycompany;

    import Java.io.IOException;
    import org.Apache.commons.lang3.StringUtils;
    import com.fasterxml.jackson.core.JsonParser;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.DeserializationContext;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
    import com.fasterxml.jackson.databind.module.SimpleModule;

    public class MyObjectMapper extends ObjectMapper {

        public MyObjectMapper() {
            registerModule(new MyModule());
        }
    }

    class MyModule extends SimpleModule {

        public MyModule() {
            addDeserializer(String.class, new StdScalarDeserializer<String>  (String.class) {
                @Override
                public String deserialize(JsonParser jp, DeserializationContext  ctxt) throws IOException,
                    JsonProcessingException {
                    return StringUtils.trim(jp.getValueAsString());
                }
            });
        }
    }

Mettez à jour le servlet-context.xml de Spring:

<bean id="objectMapper" class="com.mycompany.MyObjectMapper" />

    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper" ref="objectMapper" />
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
15
walv

Pour Spring Boot, il suffit de créer un désérialiseur personnalisé comme documenté dans le manuel .

Ce qui suit est mon code Groovy mais n'hésitez pas à l'adapter pour qu'il fonctionne en Java.

import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer
import org.springframework.boot.jackson.JsonComponent

import static com.fasterxml.jackson.core.JsonToken.VALUE_STRING

@JsonComponent
class TrimmingJsonDeserializer extends JsonDeserializer<String> {

    @Override
    String deserialize(JsonParser parser, DeserializationContext context) {
        parser.hasToken(VALUE_STRING) ? parser.text?.trim() : null
    }
}
5
adarshr

com.fasterxml.jackson.dataformat

pom.xml

   <dependency>
      <groupId>com.fasterxml.jackson.dataformat</groupId>
      <artifactId>jackson-dataformat-csv</artifactId>
      <version>2.5.3</version>
    </dependency>

CsvUtil.Java

     CsvSchema bootstrapSchema = CsvSchema.emptySchema().withHeader().sortedBy();
     CsvMapper mapper = new CsvMapper();
     mapper.enable(CsvParser.Feature.TRIM_SPACES);
     InputStream inputStream = ResourceUtils.getURL(fileName).openStream();
     MappingIterator<T> readValues =
          mapper.readerFor(type).with(bootstrapSchema).readValues(inputStream);
4
vanduc1102

Je pense qu'il est préférable d'étendre StringDeserializer par défaut car il gère déjà certains cas spécifiques (voir ici et ici ) qui peuvent être utilisés par des bibliothèques tierces. Vous trouverez ci-dessous la configuration de Spring Boot.

@JsonComponent
public class StringDeserializer extends com.fasterxml.jackson.databind.deser.std.StringDeserializer {

    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        String value = super.deserialize(p, ctxt);
        return value != null ? value.trim() : null;
    }
}
0
Eugene Maysyuk