Jackson Mapper 后期构建

问题描述 投票:0回答:4

我正在使用 Jackson

ObjectMapper
将一些 JSON 反序列化为 Java 类,我们将其称为
PlayerData
。我想向
PlayerData
类添加一些逻辑,以在加载字段后修复一些数据。例如,一些早期的 JSON 文件过去使用“性别”标志而不是“性别”标志,所以如果设置了性别标志但未设置性别标志,我想将性别字段的值设置为性别字段的值。

是否可以将某种 @PostConstruct 或 @AfterLoad 注释附加到方法上?或者也许是我可以实现的接口?我没有注意到文档中的一个,但这似乎是一个明显的功能。

java mapping jackson
4个回答
26
投票

通过评论中的link找到了这个(来源:fedor.belov)。这似乎允许您运行构建后的代码。

为通过以下方式到达这里的人添加评论 http://jira.codehaus.org/browse/JACKSON-645http://jira.codehaus.org/browse/JACKSON-538并且正在寻找 解串器完成后调用的方法。我以前可以 通过包含注释并编写来达到预期的效果 转换器使用相同的类作为输入和输出。

@JsonDeserialize(converter=MyClassSanitizer.class)  // invoked after class is fully deserialized
public class MyClass {
    public String field1;
}

import com.fasterxml.jackson.databind.util.StdConverter;
public class MyClassSanitizer extends StdConverter<MyClass,MyClass> {
  @Override
  public MyClass convert(MyClass var1) {
    var1.field1 = munge(var1.field1);
    return var1;
  }
}

11
投票

不支持开箱即用,但您可以轻松地为反序列化后调用的方法创建

@JsonPostDeserialize
注释。

首先定义注解:

/**
 * Annotation for methods to be called directly after deserialization of the object.
 */
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface JsonPostDeserialize {
}

然后,将以下注册和实现代码添加到您的项目中:

public static void addPostDeserializeSupport(ObjectMapper objectMapper) {
    SimpleModule module = new SimpleModule();
    module.setDeserializerModifier(new BeanDeserializerModifier() {
        @Override
        public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDescription,
                JsonDeserializer<?> originalDeserializer) {
            return new CustomAnnotationsDeserializer(originalDeserializer, beanDescription);
        }
    });
    objectMapper.registerModule(module);
}

/**
 * Class implementing the functionality of the {@link JsonPostDeserialize} annotation.
 */
public class CustomAnnotationsDeserializer extends DelegatingDeserializer {
    private final BeanDescription beanDescription;

    public CustomAnnotationsDeserializer(JsonDeserializer<?> delegate, BeanDescription beanDescription) {
        super(delegate);
        this.beanDescription = beanDescription;
    }

    @Override
    protected JsonDeserializer<?> newDelegatingInstance(JsonDeserializer<?> newDelegatee) {
        return new CustomAnnotationsDeserializer(newDelegatee, beanDescription);
    }

    @Override
    public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        Object deserializedObject = super.deserialize(p, ctxt);

        callPostDeserializeMethods(deserializedObject);
        return deserializedObject;
    }

    private void callPostDeserializeMethods(Object deserializedObject) {
        for (AnnotatedMethod method : beanDescription.getClassInfo().memberMethods()) {
            if (method.hasAnnotation(JsonPostDeserialize.class)) {
                try {
                    method.callOn(deserializedObject);
                } catch (Exception e) {
                    throw new RuntimeException("Failed to call @JsonPostDeserialize annotated method in class "
                            + beanDescription.getClassInfo().getName(), e);
                }
            }
        }
    }
}

最后,用

ObjectMapper
修改你的
addPostDeserializeSupport
实例,它将调用反序列化对象的所有
@JsonPostDeserialize
带注释的方法。


11
投票

如果您不使用

@JsonCreator
,那么 Jackson 将使用 setter 和 getter 方法来设置字段。

因此,如果您定义以下方法,假设您有

TimeOfDay
LightLevel
枚举:

@JsonProperty("timeOfDay")
public void setTimeOfDay(final TimeOfDay timeOfDay) {
  this.timeOfDay = timeOfDay;
  if (lightLevel == null) {
    lightLevel = (timeOfDay == TimeOfDay.DAY) ? LightLevel.LIGHT : LightLevel.DARK;
  }
}

@JsonProperty("lightLevel")
public void setLightLevel(final LightLevel lightLevel) {
  this.lightLevel = lightLevel;
  if (timeOfDay == null) {
    timeOfDay = (lightLevel == LightLevel.LIGHT) ? TimeOfDay.DAY : TimeOfDay.NIGHT;
  }
}

它会起作用的。

更新:您可以在这里找到Jackson库的所有注释。

更新2:其他解决方案:

class Example {
  private final TimeOfDay timeOfDay;
  private final LightLevel lightLevel;
  
  @JsonCreator
  public Example(@JsonProperty("timeOfDay") final TimeOfDay timeOfDay) {
    super();
    this.timeOfDay = timeOfDay;
    this.lightLevel = getLightLevelByTimeOfDay(timeOfDay)
  }

  @JsonFactory
  public static Example createExample(@JsonProperty("lightLevel") final LightLevel lightLevel) {
    return new Example(getTimeOfDayByLightLevel(lightLevel));
  }
  
  private static TimeOfDay getTimeOfDayByLightLevel(final LightLevel) {
    return (lightLevel == LightLevel.LIGHT) ? TimeOfDay.DAY : TimeOfDay.NIGHT;
  }
  
  private static LightLevel getLightLevelByTimeOfDay(final TimeOfDay) {
    return (timeOfDay == TimeOfDay.DAY) ? LightLevel.LIGHT : LightLevel.DARK;
  }

}

4
投票

这实际上已经被建议过几次了。因此,也许提交 RFE 是有意义的;有多种方法可以实现这一点:显而易见的是能够注释类型(@JsonPostProcess(Processor.class))以及通过模块API注册后处理器的能力(这样当Jackson构造反序列化器时基本上就会有一个回调,让模块指定要使用的后处理器(如果有)。但也许有更好的方法来做到这一点。

© www.soinside.com 2019 - 2024. All rights reserved.