【问题标题】:Gson and Jackson incompatibilityGson 和 Jackson 不兼容
【发布时间】:2015-03-18 16:20:34
【问题描述】:

目前我在做两个项目:一个是使用 Java 1.5 和 Spring 3.2.3,另一个是使用 Java 1.8 和 Spring 4.1.5。您可能知道,由于 Spring 4.x.x 默认 HttpMessageConverter 是 Gson。在 4.x.x 版本之前默认是 Jackson。我认为这不会影响兼容性,但你瞧:它确实...具体来说,我在谈论 java.util.Date 解析/映射。

假设我有一个如下类(equals()hashCode() 方法被省略):

public class FooBar {
    public String str = getRandomStringEncodedInUTF8();
    public Date date = new Date(0); //1970-01-01 00:00:00
}

public void retrieveFooBars() {
    Type collectionType = new TypeToken<HashSet<FooBar>>(){}.getType();
    Gson gson = new GsonBuilder().
        registerTypeAdapter(FooBar.class, new FooBarDeserializer()).create();

    String ipAddress = getIpAddress();
    RestTemplate request = new RestTemplate();

    String gsonString;
    try {
        gsonString = request.getForObject("http://"
            + ipAddress+ ":8080/foobars/get", String.class);
    } catch (Exception e) {
        return;
    }

    Set<FooBar> fooBars = gson.fromJson(gsonString, collectionType);
}

FooBarDeserializer如下:

public class FooBarDeserializer implements JsonDeserializer<FooBar> {

    private final DateTimeFormatter dateFormatter = DateTimeFormat
        .forPattern("yyyy-MM-dd");

    @Override
    public FooBar deserialize(JsonElement element, Type type,
            JsonDeserializationContext context) throws JsonParseException {

        FooBar fooBar = new FooBar();
        JsonObject obj = element.getAsJsonObject();

        JsonElement str = obj.get("str");
        if (str != null)
        if (!str.isJsonNull()) {
            fooBar.str = originalId.getAsString();
        }

        JsonElement date = obj.get("date");
        if (date != null)
        if (!date.isJsonNull()) {
            String temp = date.getAsString();

            if (temp != null) {
                fooBar.date = dateFormatter.parseDateTime(temp).toDate();
            }
        }
        return FooBar;
    }
}

如果我不使用FooBarDeserializer,我会得到com.google.gson.JsonSyntaxException: 1970-01-01,原因是:java.text.ParseException: Unparseable date: "1970-01-01" 到目前为止一切顺利。我可以接收数据,并保留我的 UTF-8 编码。

问题是使用旧版本的 Spring 向服务器发送数据时:

public void distributeFooBars() {
    Gson gson = new GsonBuilder()
        .registerTypeAdapter(FooBar.class, new FooBarSerializer()).create();

    String ipAddress = getIpAddress();
    Set<FooBar> newFooBars = getNewFooBars();

    RestTemplate request = new RestTemplate();
    String gsonString = gson.toJson(newFooBars);
    try {
        request.postForObject("http://" + ipAddress + ":8080/foobars/set",
            gsonString, Boolean.class);
    } catch (Exception e) {
        return;
    }
}

如果我在将 newFooBars 发送到“旧”服务器之前没有明确使用 Gson,并让 GsonHttpMessageConverter 完成它的工作,我会保留我的 UTF-8 字符串,但解析日期失败,因为它被写为“jan 1, 1970”。如果我在发送之前明确使用 Gson 并将 newFooBars 转换为 String,我会丢失 UTF-8 字符(即,我会收到 ?????_?????? 而不是 ČĆĐŠŽ_čćđšž),但解析日期不会抛出一个例外。客户序列化程序是:

public class FooBarSerializer implements JsonSerializer<FooBar> {

    private final DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");

    @Override
    public JsonElement serialize(FooBar fooBar, Type type,
            JsonSerializationContext context) {

        JsonObject obj = new JsonObject();
        obj.addProperty("str", fooBar.str);

        String date = null;
        if (fooBar.date != null) {
            date = formatter.print(fooBar.date.getTime());
        }
        obj.addProperty("date", date);

        return obj;
    }
}

旁注:如果显式使用 Gson,则在较旧的服务器控制器上具有以下映射:

@RequestMapping(value = "/set", method = RequestMethod.POST, consumes="text/plain;charset=UTF-8")

否则:

@RequestMapping(value = "/set", method = RequestMethod.POST, consumes="application/json;charset=UTF-8")

否则我会得到 415 Unsupported media type。

我知道这是一篇很长的帖子,但我会感谢任何可以帮助我的人。所以我的问题是:

有没有办法解决这个问题?就像拥有一个系统范围的 Gson 序列化器,但仅适用于 java.util.Date。或者设置强制对纯文本/文本进行 UTF-8 编码?

【问题讨论】:

  • Jackson 在 Spring 4 中仍然是首选。至少如果你使用默认的 @EnableWebMvc 配置是这样。
  • @SotiriosDelimanolis 你能详细说明一下吗?我的项目中有 spring-webmvc 作为 maven 依赖项。另外,我的 servlet.xml 文件中有 标记。但是,我仍然得到RestTemplate:770 - Writing [[FooBar [str=ČĆĐŠŽ_čćđšž, date=... using ... GsonHttpMessageConverter
  • 可能与您的问题无关,但如果 Jackson 在您的类路径中,则无论 Gson 是否在您的类路径中,都会注册 Jackson HttpMessageConverter。如果 Jackson 不是而 Gson 是,则 Gson HttpMessageConverter 将被注册。
  • 我两个都有:jackson-core-2.5.1gson-2.3.1。有没有办法强制JacksonHttpMessageConverter 超过 Gson?

标签: java spring utf-8 jackson gson


【解决方案1】:

从我的构建路径中排除 gson-2.3.1.jar 后,Jackson2HttpMessageConverter 成为默认值。 Gson 是常见代码的依赖项,因此我在 pom.xml 中添加了:

<dependency>
    <groupId>com.example</groupId>
    <artifactId>common_code</artifactId>
    <version>6.9</version>

    <exclusions>
        <exclusion>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
        </exclusion>
    </exclusions>
</dependency>

在那之后,问题不是 Date 对象而是 joda DateTime 对象。添加 jackson-datatype-joda 作为 maven 依赖项解决了这个问题:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-joda</artifactId>
    <version>${jackson.version}</version>
</dependency>

现在一切正常... Sotirios Delimanolis 感谢您的帮助。

【讨论】:

    【解决方案2】:

    我知道有两种方法可以处理这个问题(从 GSON 2.8.2 开始):

    1 告诉 GSON 要使用哪种模式:

    https://static.javadoc.io/com.google.code.gson/gson/2.8.2/com/google/gson/GsonBuilder.html#setDateFormat-java.lang.String-

    Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd").create();
    

    2 为 Date 类型创建一个新的 DateSerializer:

    public class DateDeserializer implements JsonDeserializer<Date> {
    
    @Override
    public Date deserialize(JsonElement element, Type arg1, JsonDeserializationContext arg2) {
    
        // Do your stuff here...
    
        String dateString = element.getAsString();
        Long val = Long.valueOf(dateString);
        Timestamp timestamp = new Timestamp(val);       
        Date date = new Date(timestamp.getTime());
        return date;
      }
    
    }
    

    并使用它:

    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(Date.class, new DateDeserializer());
    

    这解决了我的问题,没有排除任何东西。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-05-18
      • 2016-06-29
      • 2018-06-05
      • 2017-10-06
      • 1970-01-01
      • 2019-01-02
      • 1970-01-01
      相关资源
      最近更新 更多