Jackson可以配置为从所有字符串属性修剪前导/尾随空格吗?

时间:2011-07-27 22:54:55

标签: java json spring-mvc jackson

示例JSON(请注意该字符串具有尾随空格):

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

理想情况下,反序列化的实例将具有 aString 属性,其值为“string”(即没有尾随空格)。这似乎是可能支持的东西,但我找不到它(例如在 DeserializationConfig.Feature 中)。

我们正在使用Spring MVC 3.x,因此基于Spring的解决方案也可以。

我尝试根据forum post中的建议配置Spring的WebDataBinder,但在使用Jackson消息转换器时它似乎不起作用:

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

6 个答案:

答案 0 :(得分:18)

Spring Boot用户的简单解决方案,只需将walv的SimpleModule扩展添加到您的应用程序上下文中:

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();
            }
        });
    }
}
  

自定义Jackson的另一种方法是将com.fasterxml.jackson.databind.Module类型的bean添加到您的上下文中。它们将在ObjectMapper类型的每个bean中注册,为您的应用程序添加新功能时提供全局机制来提供自定义模块。

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

如果你使用spring boot,你必须自己注册StringTrimModule(你不需要用@Component注释)

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

答案 1 :(得分:15)

使用custom deserializer,您可以执行以下操作:

 <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();
     }
 }

这个解决方案确实暗示这个bean属性将始终以这种方式序列化,并且您必须以这种方式注释要反序列化的每个属性。

答案 2 :(得分:13)

注释@JsonDeserialize的问题是你必须始终记得把它放在setter上。 为了使用Spring MVC全局“永远”,我做了下一步:

的pom.xml:

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

创建自定义ObjectMapper:

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());
                }
            });
        }
    }

更新Spring的servlet-context.xml:

<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>

答案 3 :(得分:6)

我认为最好扩展默认的StringDeserializer,因为它已经处理了第三方库可以使用的某些特定情况(请参见herehere)。您可以在下面找到Spring Boot的配置。这仅适用于Jackson 2.9.0及更高版本,因为从2.9.0版本开始,StringDeserializer不再是最终版本。如果您的Jackson版本低于2.9.0,则仍可以将StringDeserializer的内容复制到代码中以处理上述情况。

@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;
    }
}

答案 4 :(得分:3)

对于Spring Boot,我们只需要创建一个自定义反序列化器documented in the manual

以下是我的Groovy代码,但可以随意使其适用于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 :(得分:2)

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);