Jackson 是否可以配置为修剪所有字符串属性中的前导/尾随空格?

发布于 2024-11-26 16:55:23 字数 640 浏览 0 评论 0 原文

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

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

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

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

我尝试根据论坛帖子中的建议配置Spring的WebDataBinder但使用 Jackson 消息转换器时它似乎不起作用:

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

Example JSON (note that the string has trailing spaces):

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

Ideally, the deserialised instance would have an aString property with a value of "string" (i.e. without trailing spaces). This seems like something that is probably supported but I can't find it (e.g. in DeserializationConfig.Feature).

We're using Spring MVC 3.x so a Spring-based solution would also be fine.

I tried configuring Spring's WebDataBinder based on a suggestion in a forum post but it does not seem to work when using a Jackson message converter:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

烙印 2024-12-03 16:55:23

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

如果您不使用弹簧boot,你必须自己注册StringTrimModule(不需要用@Component注解)

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

Easy solution for Spring Boot users, just add that walv's SimpleModule extension to your application context:

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

Another way to customize Jackson is to add beans of type com.fasterxml.jackson.databind.Module to your context. They will be registered with every bean of type ObjectMapper, providing a global mechanism for contributing custom modules when you add new features to your application.

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

if you are not using spring boot, you have to register the StringTrimModule yourself (you do not need to annotate it with @Component)

<bean class="org.springframework.http.converter.json.Jackson2Objec‌​tMapperFactoryBean">
    <property name="modulesToInstall" value="com.example.StringTrimModule"/>
</bean
是你 2024-12-03 16:55:23

使用 自定义反序列化器< /a>,您可以执行以下操作:

 <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 属性将始终以这种方式序列化,并且您必须注释您想要的每个属性以这种方式反序列化。

With a custom deserializer, you could do the following:

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

This solution does imply that this bean attribute will always be serialized this way, and you will have to annotate every attribute that you want to be deserialized this way.

摇划花蜜的午后 2024-12-03 16:55:23

我认为最好扩展默认的 StringDeserializer,因为它已经处理了一些特定的情况(请参阅 此处此处)可供第三方库使用。您可以在下面找到 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;
    }
}

I think it is better to extend default StringDeserializer as it already handles some specific cases (see here and here) that can be used by third party libraries. Below you can find configuration for Spring Boot. This is possible only with Jackson 2.9.0 and above as starting from 2.9.0 version StringDeserializer is not final anymore. If you have Jackson version below 2.9.0 you can still copy content of StringDeserializer to your code to handle above mentioned cases.

@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;
    }
}
盗琴音 2024-12-03 16:55:23

注解@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>

The problem of annotation @JsonDeserialize is that you must always remember to put it on the setter.
To make it globally "once and forever" with Spring MVC, I did next steps:

pom.xml:

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

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

Update Spring's 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>
蓝天白云 2024-12-03 16:55:23

对于 Spring Boot,我们只需创建一个自定义反序列化器,如 记录在手册中

以下是我的 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
    }
}

For Spring Boot, we just have to create a custom deserializer as documented in the manual.

The following is my Groovy code but feel free to adapt it to work in 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
    }
}
知足的幸福 2024-12-03 16:55:23

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

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);
夜夜流光相皎洁 2024-12-03 16:55:23

我建议您执行以下操作:

首先,创建一个模块来修剪并将其放入一个类中:

import java.io.IOException;

import org.springframework.stereotype.Component;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;

@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 {
                return jsonParser.getValueAsString().trim();
            }
        });
    }
}

然后,创建一个类来配置 jackson 并添加该模块:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * Class used to configure Jackson
 */
@Configuration
public class JacksonConfiguration {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new StringTrimModule());
        return mapper;
    }
}

就是这样。

I propose you the following:

First, create a module to trim and put it into a class:

import java.io.IOException;

import org.springframework.stereotype.Component;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;

@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 {
                return jsonParser.getValueAsString().trim();
            }
        });
    }
}

Then, create a class to configure jackson and add the module:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * Class used to configure Jackson
 */
@Configuration
public class JacksonConfiguration {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new StringTrimModule());
        return mapper;
    }
}

That's it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文