Java Jackson-如何替换序列化映射
我需要将一个包含列表和映射的图形序列化为JSON。每个映射实例都包含一个UUID字段。该图可以包含多个具有相同UUID的映射实例。具有相同UUID的映射被认为是相同的 在序列化过程中,我希望替换以前仅由其UUID序列化的映射实例 与杰克逊一起实现这一目标的最佳方式是什么Java Jackson-如何替换序列化映射,java,jackson,Java,Jackson,我需要将一个包含列表和映射的图形序列化为JSON。每个映射实例都包含一个UUID字段。该图可以包含多个具有相同UUID的映射实例。具有相同UUID的映射被认为是相同的 在序列化过程中,我希望替换以前仅由其UUID序列化的映射实例 与杰克逊一起实现这一目标的最佳方式是什么 谢谢您可以为图形类实现自定义序列化程序 public class IdentifiableSerializerTest { public static void main(String[] args) throws J
谢谢您可以为图形类实现自定义序列化程序
public class IdentifiableSerializerTest {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = createObjectMapper();
test(mapper);
}
interface Identifiable {
Long getId();
}
public static ObjectMapper createObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
// disable quoting - for testing purpose
mapper.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, false);
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
// register serializer for Identifiable type
SimpleModule module = new SimpleModule();
module.addSerializer(Identifiable.class, new IdentifiableSerializer(mapper.writer()));
mapper.registerModule(module);
// lifecycle hook to re-init IdentifiableSerializer on root-level serialize calls
mapper.setSerializerProvider(new IdentifiableSerializerProvider());
return mapper;
}
/**
* This class serves to intercept root-level serialize calls in order to
* clean the map of visited objects, see {@link IdentifiableSerializer#visited}.
*
* TODO: this seems lot of code just to get a hook on root-level serialize calls...
*/
public static class IdentifiableSerializerProvider extends DefaultSerializerProvider {
public IdentifiableSerializerProvider() { super(); }
protected IdentifiableSerializerProvider(SerializerProvider src, SerializationConfig config, SerializerFactory f) {
super(src, config, f);
}
@Override
public DefaultSerializerProvider createInstance(SerializationConfig config, SerializerFactory f) {
return new IdentifiableSerializerProvider(this, config, f);
}
@Override
public void serializeValue(JsonGenerator gen, Object value) throws IOException {
IdentifiableSerializer.reset();
super.serializeValue(gen, value);
}
}
public static class IdentifiableSerializer extends JsonSerializer<Identifiable> {
private static ThreadLocal<Set> visited = new ThreadLocal<Set>() {
@Override
protected Set initialValue() {
return new HashSet();
}
};
public static void reset() {
visited.get().clear();
}
private final ObjectWriter delegate;
public IdentifiableSerializer(ObjectWriter delegate) {
this.delegate = delegate;
}
@Override
public void serialize(Identifiable value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
Long id = value.getId();
Set seen = visited.get();
if (seen.contains(id)) {
jgen.writeStartObject();
jgen.writeNumberField("@REF", id);
jgen.writeEndObject();
}
else {
seen.add(id);
delegate.writeValue(jgen, value);
}
}
}
static class IdentifiableMap extends HashMap implements Identifiable {
static long counter = 0;
Long id = counter++;
{
put("@ID", id);
}
@Override
public Long getId() {
return id;
}
}
public static void test(ObjectMapper mapper) throws JsonProcessingException {
Map myMap = new IdentifiableMap() {{
put("key1", 1);
put("key2", 2);
put("key3", 3);
}};
List<Map> myList = Arrays.asList(myMap, myMap);
String expected = "[{key1:1,key2:2,key3:3,@ID:0},{@REF:0}]";
String actual = mapper.writeValueAsString(myList);
Assert.assertEquals(expected, actual);
System.out.println("SUCCESS");
}
}
您必须扩展StdSerializer并覆盖
@Override
public void serialize(T value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException
当你这么做的时候,你需要让jackson知道你的连载程序。您可以通过使用
@JsonSerialize(using=CustomSerializer.class)
注释图形类来实现这一点,也可以注册包含自定义序列化程序的新模块 下面是我提出的有效解决方案
但是,有没有一种更优雅的方法来获取顶级序列化调用的生命周期挂钩(重新初始化自定义序列化程序时需要用到)
另外,我不认为使用ThreadLocal跟踪每个线程访问的对象是最好的解决方案。有什么建议吗
谢谢
public class IdentifiableSerializerTest {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = createObjectMapper();
test(mapper);
}
interface Identifiable {
Long getId();
}
public static ObjectMapper createObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
// disable quoting - for testing purpose
mapper.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, false);
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
// register serializer for Identifiable type
SimpleModule module = new SimpleModule();
module.addSerializer(Identifiable.class, new IdentifiableSerializer(mapper.writer()));
mapper.registerModule(module);
// lifecycle hook to re-init IdentifiableSerializer on root-level serialize calls
mapper.setSerializerProvider(new IdentifiableSerializerProvider());
return mapper;
}
/**
* This class serves to intercept root-level serialize calls in order to
* clean the map of visited objects, see {@link IdentifiableSerializer#visited}.
*
* TODO: this seems lot of code just to get a hook on root-level serialize calls...
*/
public static class IdentifiableSerializerProvider extends DefaultSerializerProvider {
public IdentifiableSerializerProvider() { super(); }
protected IdentifiableSerializerProvider(SerializerProvider src, SerializationConfig config, SerializerFactory f) {
super(src, config, f);
}
@Override
public DefaultSerializerProvider createInstance(SerializationConfig config, SerializerFactory f) {
return new IdentifiableSerializerProvider(this, config, f);
}
@Override
public void serializeValue(JsonGenerator gen, Object value) throws IOException {
IdentifiableSerializer.reset();
super.serializeValue(gen, value);
}
}
public static class IdentifiableSerializer extends JsonSerializer<Identifiable> {
private static ThreadLocal<Set> visited = new ThreadLocal<Set>() {
@Override
protected Set initialValue() {
return new HashSet();
}
};
public static void reset() {
visited.get().clear();
}
private final ObjectWriter delegate;
public IdentifiableSerializer(ObjectWriter delegate) {
this.delegate = delegate;
}
@Override
public void serialize(Identifiable value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
Long id = value.getId();
Set seen = visited.get();
if (seen.contains(id)) {
jgen.writeStartObject();
jgen.writeNumberField("@REF", id);
jgen.writeEndObject();
}
else {
seen.add(id);
delegate.writeValue(jgen, value);
}
}
}
static class IdentifiableMap extends HashMap implements Identifiable {
static long counter = 0;
Long id = counter++;
{
put("@ID", id);
}
@Override
public Long getId() {
return id;
}
}
public static void test(ObjectMapper mapper) throws JsonProcessingException {
Map myMap = new IdentifiableMap() {{
put("key1", 1);
put("key2", 2);
put("key3", 3);
}};
List<Map> myList = Arrays.asList(myMap, myMap);
String expected = "[{key1:1,key2:2,key3:3,@ID:0},{@REF:0}]";
String actual = mapper.writeValueAsString(myList);
Assert.assertEquals(expected, actual);
System.out.println("SUCCESS");
}
}
公共类IdentifiableSerializerTest{
公共静态void main(字符串[]args)引发JsonProcessingException{
ObjectMapper映射器=createObjectMapper();
测试(制图员);
}
接口可识别{
长getId();
}
公共静态对象映射器createObjectMapper(){
ObjectMapper mapper=新的ObjectMapper();
//禁用报价-用于测试目的
配置(JsonGenerator.Feature.QUOTE\u字段名称,false);
configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES,true);
//可识别类型的注册序列化程序
SimpleModule=新的SimpleModule();
module.addSerializer(identificatable.class,新的identificatableSerializer(mapper.writer());
映射器注册表模块(模块);
//在根级别序列化调用上重新初始化IdentifiableSerializer的生命周期挂钩
setSerializerProvider(新的可识别SerializerProvider());
返回映射器;
}
/**
*此类用于拦截根级别的序列化调用,以便
*清理访问对象的映射,请参阅{@link-identiableserializer#visted}。
*
*TODO:这似乎有很多代码只是为了在根级别序列化调用上获得一个钩子。。。
*/
公共静态类IdentifiableSerializerProvider扩展了DefaultSerializerProvider{
公共可标识SerializerProvider(){super();}
受保护的可标识SerializerProvider(SerializerProvider src、SerializationConfig配置、SerializerFactory f){
超级(src,config,f);
}
@凌驾
公共DefaultSerializerProvider createInstance(SerializationConfig配置,SerializerFactory f){
返回新的IdentifiableSerializerProvider(this,config,f);
}
@凌驾
公共void序列化值(JsonGenerator gen,对象值)引发IOException{
IdentifiableSerializer.reset();
super.value(gen,value);
}
}
公共静态类IdentifiableSerializer扩展了JsonSerializer{
访问的私有静态ThreadLocal=新建ThreadLocal(){
@凌驾
受保护的集合初始值(){
返回新的HashSet();
}
};
公共静态无效重置(){
已访问。获取()。清除();
}
私人代表;
公共可标识序列化程序(ObjectWriter委托){
this.delegate=委托;
}
@凌驾
public void serialize(可识别值、JsonGenerator jgen、SerializerProvider提供程序)引发IOException{
Long id=value.getId();
Set seen=visted.get();
如果(见。包含(id)){
jgen.writeStartObject();
jgen.writeNumberField(“@REF”,id);
jgen.writeEndObject();
}
否则{
见。添加(id);
delegate.writeValue(jgen,value);
}
}
}
静态类IdentifiableMap扩展HashMap实现可识别{
静态长计数器=0;
长id=计数器++;
{
put(“@ID”,ID);
}
@凌驾
公共长getId(){
返回id;
}
}
公共静态无效测试(ObjectMapper映射器)抛出JsonProcessingException{
Map myMap=新的可识别地图(){{
放置(“键1”,1);
放置(“键2”,2);
放置(“键3”,3);
}};
List myList=Arrays.asList(myMap,myMap);
字符串应为=“[{key1:1,key2:2,key3:3,@ID:0},{@REF:0}]”;
String actual=mapper.writeValueAsString(myList);
Assert.assertEquals(预期、实际);
System.out.println(“成功”);
}
}