Java 使用jackson反序列化字符串以映射多个类型
我看到过这样的答案,它显示了如何使用将JSON字符串反序列化到一个映射,其中键/值组合不是Java 使用jackson反序列化字符串以映射多个类型,java,json,jackson,jodatime,json-deserialization,Java,Json,Jackson,Jodatime,Json Deserialization,我看到过这样的答案,它显示了如何使用将JSON字符串反序列化到一个映射,其中键/值组合不是string。在这种情况下,字符串应该反序列化为多个不同的类型,而不仅仅是一个 我意识到一个解决方案是定义我自己的类,而不使用Map,但我想知道我是否可以使用纯配置 这是我的测试代码 import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; import org.joda.time.DateTim
string
。在这种情况下,字符串应该反序列化为多个不同的类型,而不仅仅是一个
我意识到一个解决方案是定义我自己的类,而不使用Map
,但我想知道我是否可以使用纯配置
这是我的测试代码
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDateTime;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.MapType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.datatype.joda.JodaModule;
public class JodaTimeMapTest {
public static void main(final String[] args) throws Exception {
// Map with dates.
final DateTime now = new DateTime().withZone(DateTimeZone.UTC);
final LocalDateTime nowLocal = new LocalDateTime();
final LocalDateTime notNowLocal = new LocalDateTime(2007, 3, 25, 2, 30, 0);
final Map<String, Object> dateMap = new HashMap<>();
dateMap.put("now", now);
dateMap.put("nowLocal", nowLocal);
dateMap.put("notNowLocal", notNowLocal);
// Serialise map to string.
final ObjectMapper mapper = mapper();
final String dateMapJson = mapper.writeValueAsString(dateMap);
// De-serialise string to map.
final TypeFactory typeFactory = mapper.getTypeFactory();
final MapType mapType = typeFactory.constructMapType(HashMap.class, String.class, Object.class);
final HashMap<String, Object> dateMapFromJson = mapper.readValue(dateMapJson, mapType);
// First one has dates, second has strings.
printMap(dateMap);
printMap(dateMapFromJson);
}
private static void printMap(final Map<String, Object> map) {
System.out.println(map.entrySet().stream().map(entry -> {
return entry.getKey() + ", type = " + entry.getValue().getClass().getName() + ", value = " + entry.getValue();
}).collect(Collectors.joining(System.lineSeparator())));
}
private static ObjectMapper mapper() {
final ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JodaModule());
mapper.configure(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return mapper;
}
}
样品溶液
基于,这里有一个适合我的解决方案。在我的示例中,我只需要map键
就可以确定值
是什么类型的Joda日期/时间类
首先是我对澳大利亚人告诉我的反序列化程序的实现
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import org.joda.time.DateTime;
import org.joda.time.LocalDateTime;
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.StdDeserializer;
/** De-serialise values from a map that contains Joda times and strings. */
public class JodaMapDeserialiser extends StdDeserializer<Object> {
/** Mapping between keys in the map to a type of Joda time. */
enum DateType {
DATE_TIME("now"), LOCAL_DATE_TIME("notNowLocal", "nowLocal");
final List<String> keys;
DateType(final String... keys) {
this.keys = Arrays.asList(keys);
}
public static DateType forKeyString(final String keyString) {
return Stream.of(values()).filter(dateTypes -> dateTypes.keys.contains(keyString)) //
.findFirst().orElse(null);
}
}
public JodaMapDeserialiser() {
super(Object.class);
}
@Override
public Object deserialize(final JsonParser p, final DeserializationContext ctxt)
throws IOException, JsonProcessingException {
// Each entry in the map has a key and value.
final String value = p.readValueAs(String.class);
final String key = p.getCurrentName();
// Convert the value depending on what the key is.
switch (DateType.forKeyString(key)) {
case DATE_TIME:
return DateTime.parse(value);
case LOCAL_DATE_TIME:
return LocalDateTime.parse(value);
default:
return value;
}
}
}
最后,我的maven依赖项(joda时间包含在jackson数据类型joda
中)
com.fasterxml.jackson.core
.由于您注册的Jodamodule:“now”:“2018-05-04T11:42:15.454Z”,您的日期对象被序列化为字符串
当您反序列化Json字符串时,您需要一个带有字符串键和对象值的哈希映射。Jackson怎么知道这些对象应该是不同类型的日期,它只看到字符串
您可以为此创建一个自定义反序列化程序,并实现正确反序列化每个日期的逻辑(例如,您可以通过regex确定类型)
有这样的例子吗?我还找不到任何东西可以告诉我如何使用hey名称作为eaxample。我在回答中加入了一个我刚刚编好的示例,以便您可以看到如何创建和注册它。添加此项后,ObjectMapper将使用此反序列化器反序列化所有对象类。我在本例中想到的是,我将使用正则表达式区分日期类型,这样您就不必关心键了谢谢!对于我的例子,我只需要测试键就可以知道值应该是什么(因为在真正的代码中,有很多条目,只有两三个条目包含日期,其余的都可以作为字符串)。我已经根据您的建议更新了我的问题和答案@aussie-非常感谢:D
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import org.joda.time.DateTime;
import org.joda.time.LocalDateTime;
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.StdDeserializer;
/** De-serialise values from a map that contains Joda times and strings. */
public class JodaMapDeserialiser extends StdDeserializer<Object> {
/** Mapping between keys in the map to a type of Joda time. */
enum DateType {
DATE_TIME("now"), LOCAL_DATE_TIME("notNowLocal", "nowLocal");
final List<String> keys;
DateType(final String... keys) {
this.keys = Arrays.asList(keys);
}
public static DateType forKeyString(final String keyString) {
return Stream.of(values()).filter(dateTypes -> dateTypes.keys.contains(keyString)) //
.findFirst().orElse(null);
}
}
public JodaMapDeserialiser() {
super(Object.class);
}
@Override
public Object deserialize(final JsonParser p, final DeserializationContext ctxt)
throws IOException, JsonProcessingException {
// Each entry in the map has a key and value.
final String value = p.readValueAs(String.class);
final String key = p.getCurrentName();
// Convert the value depending on what the key is.
switch (DateType.forKeyString(key)) {
case DATE_TIME:
return DateTime.parse(value);
case LOCAL_DATE_TIME:
return LocalDateTime.parse(value);
default:
return value;
}
}
}
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDateTime;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.MapType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.datatype.joda.JodaModule;
public class JodaTimeMapTest {
public static void main(final String[] args) throws Exception {
// Map with dates.
final DateTime now = new DateTime().withZone(DateTimeZone.UTC);
final LocalDateTime nowLocal = new LocalDateTime();
final LocalDateTime notNowLocal = new LocalDateTime(2007, 3, 25, 2, 30, 0);
final Map<String, Object> dateMap = new HashMap<>();
dateMap.put("now", now);
dateMap.put("nowLocal", nowLocal);
dateMap.put("notNowLocal", notNowLocal);
// Serialise map to string.
final ObjectMapper mapper = mapper();
final String dateMapJson = mapper.writeValueAsString(dateMap);
// De-serialise string to map.
final TypeFactory typeFactory = mapper.getTypeFactory();
final MapType mapType = typeFactory.constructMapType(HashMap.class, String.class, Object.class);
final HashMap<String, Object> dateMapFromJson = mapper.readValue(dateMapJson, mapType);
// First one has dates, second has strings.
System.out.println("Actual map.");
printMap(dateMap);
System.out.println("Map de-serialised from JSON.");
printMap(dateMapFromJson);
System.out.println("Maps are equal: " + dateMap.equals(dateMapFromJson));
}
private static void printMap(final Map<String, Object> map) {
System.out.println(map.entrySet().stream().map(entry -> {
return " " + entry.getKey() + ", type = " + entry.getValue().getClass().getName() + ", value = "
+ entry.getValue();
}).collect(Collectors.joining(System.lineSeparator())));
}
private static ObjectMapper mapper() {
final ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JodaModule());
mapper.configure(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
final SimpleModule dateDeserializerModule = new SimpleModule();
dateDeserializerModule.addDeserializer(Object.class, new JodaMapDeserialiser());
mapper.registerModule(dateDeserializerModule);
return mapper;
}
}
Actual map.
now, type = org.joda.time.DateTime, value = 2018-05-05T04:03:20.684Z
notNowLocal, type = org.joda.time.LocalDateTime, value = 2007-03-25T02:30:00.000
nowLocal, type = org.joda.time.LocalDateTime, value = 2018-05-05T14:03:20.809
Map de-serialised from JSON.
now, type = org.joda.time.DateTime, value = 2018-05-05T04:03:20.684Z
notNowLocal, type = org.joda.time.LocalDateTime, value = 2007-03-25T02:30:00.000
nowLocal, type = org.joda.time.LocalDateTime, value = 2018-05-05T14:03:20.809
Maps are equal: true
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>
<version>2.9.5</version>
</dependency>
public class MyDateDeserializer extends StdDeserializer<Object> {
public MyDateDeserializer() {
super(Object.class);
}
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
return convertStringToTheProperDate(p.readValueAs(String.class));
}
private Object convertStringToTheProperDate(String dateAsString) {
// implement the logic to convert the string to the proper type
return null;
}
}
SimpleModule dateDeserializerModule = new SimpleModule();
dateDeserializerModule.addDeserializer(Object.class, new MyDateDeserializer());
mapper.registerModule(dateDeserializerModule);