如何从JSON反序列化到LocalDate,同时还使用JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS?

时间:2014-06-26 14:11:20

标签: java json spring spring-mvc jackson

我已启用 jackson-datatype-joda ,但它无法使用JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS

我已将ObjectMapper设置如下:

ObjectMapper jacksonObjectMapper = new ObjectMapper();
jacksonObjectMapper.configure(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS, true);
jacksonObjectMapper.registerModule(new JodaModule());

反序列化时(使用 com.fasterxml.jackson.datatype.joda.deser.LocalDateDeserializer ),我最终得到错误:

org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Current token (VALUE_STRING) not numeric, can not use numeric value accessors
at [...]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Current token (VALUE_STRING) not numeric, can not use numeric value accessors

我调试了 LocalDateDeserializer ,发现它正在等待 VALUE_NUMBER_INT - 而JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS使得日期有字符串。

当我关闭该功能时,一切都很好。

此功能(或类似功能)是否有可能与Joda Time一起使用?

1 个答案:

答案 0 :(得分:4)

Joda模块使用com.fasterxml.jackson.datatype.joda.deser.LocalDateDeserializer反序列化org.joda.time.LocalDate个对象。这个类看起来像这样:

@Override
public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt)
    throws IOException, JsonProcessingException
{
    // [yyyy,mm,dd]
    if (jp.isExpectedStartArrayToken()) {
        jp.nextToken(); // VALUE_NUMBER_INT 
        int year = jp.getIntValue(); 
        jp.nextToken(); // VALUE_NUMBER_INT
        int month = jp.getIntValue();
        jp.nextToken(); // VALUE_NUMBER_INT
        int day = jp.getIntValue();
        if (jp.nextToken() != JsonToken.END_ARRAY) {
            throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY, "after LocalDate ints");
        }
        return new LocalDate(year, month, day);
    }
    switch (jp.getCurrentToken()) {
    case VALUE_NUMBER_INT:
        return new LocalDate(jp.getLongValue());            
    case VALUE_STRING:
        String str = jp.getText().trim();
        if (str.length() == 0) { // [JACKSON-360]
            return null;
        }
        return parser.parseLocalDate(str);
    default:
    }
    throw ctxt.wrongTokenException(jp, JsonToken.START_ARRAY, "expected JSON Array, String or Number");
}

如您所见,如果LocalDate是数组,则此反序列化器需要此数组中的整数。您可以编写自定义反序列化程序并扩展此实现。见下面的例子:

class LocalDateWithStringsDeserializer extends LocalDateDeserializer {

    private static final long serialVersionUID = 1L;

    @Override
    public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException,
            JsonProcessingException {
        // [yyyy,mm,dd] or ["yyyy","mm","dd"]
        if (jp.isExpectedStartArrayToken()) {
            return parseArray(jp, ctxt);
        }

        return super.deserialize(jp, ctxt);
    }

    private LocalDate parseArray(JsonParser jp, DeserializationContext ctxt)
            throws JsonParseException, IOException {
        int year = getNextValue(jp);
        int month = getNextValue(jp);
        int day = getNextValue(jp);
        if (jp.nextToken() != JsonToken.END_ARRAY) {
            throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY, "after LocalDate ints");
        }
        return new LocalDate(year, month, day);
    }

    private int getNextValue(JsonParser jp) throws IOException, JsonParseException {
        jp.nextToken();

        return new Integer(jp.getText());
    }
}

现在您可以链接上面的反序列化器和您的属性:

@JsonDeserialize(using = LocalDateWithStringsDeserializer.class)
private LocalDate date;