杰克逊·映射器(Jackson Mapper)构建后

发布于 2024-11-26 04:09:02 字数 324 浏览 1 评论 0原文

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

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

I am using the Jackson ObjectMapper to deserialize some JSON into a Java class, which we'll call PlayerData. I would like to add a bit of logic to the PlayerData class to fix up some data after the fields have been loaded in. For example, some early JSON files used to use a "sex" flag instead of a "gender" falg, so if the sex flag is set but the gender flag is not set, I'd like to set the value of the gender field to be the value of the sex field.

Is there some sort of @PostConstruct or @AfterLoad annotation that I could affix to a method? Or perhaps an interface that I could implement? I didn't notice one in the documentation, but it seemed like an obvious feature.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

风吹短裙飘 2024-12-03 04:09:02

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

为通过以下方式到达这里的人添加评论
http://jira.codehaus.org/browse/JACKSON-645
http://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;
  }
}

Found this thru a link in the comments (credit: fedor.belov). This appears to allow you to run code post construct.

Adding a comment for people who end up here via
http://jira.codehaus.org/browse/JACKSON-645 or
http://jira.codehaus.org/browse/JACKSON-538 and are looking for a
method which is called after a deserializer completes. I was able to
achieve the desired effect by including an annotation and writing a
converter which uses the same class as input and output.

@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;
  }
}
嘿嘿嘿 2024-12-03 04:09:02

不支持开箱即用,但您可以轻松创建 @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);
                }
            }
        }
    }
}

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

This is not supported out of the box, but you can easily create your @JsonPostDeserialize annotation for methods to be called after deserialization.

First, define the annotation:

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

Then, add the following registration and implementation code to your project:

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);
                }
            }
        }
    }
}

Finally, modify your ObjectMapper instance with addPostDeserializeSupport, it will invoke all @JsonPostDeserialize annotated method of deserialized objects.

写给空气的情书 2024-12-03 04:09:02

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

因此,如果您定义以下方法,假设您有 TimeOfDayLightLevel 枚举:

@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;
  }

}

If you're not using the @JsonCreator, then Jackson will use the setter and getter methods to set the fields.

So if you define the following methods assuming that you have TimeOfDay and LightLevel enums:

@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;
  }
}

it would work.

Update: You can find all of the annotations of Jackson library here.

Update2: Other solution:

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;
  }

}
秋叶绚丽 2024-12-03 04:09:02

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

This is something that has actually been suggested couple of times earlier. So maybe filing an RFE would make sense; there are multiple ways in which this could work: obvious ones being ability to annotate type (@JsonPostProcess(Processor.class)) and ability to register post-processor through Module API (so that there's basically a callback when Jackson constructs deserializer, to let module specify post-processor to use if any). But perhaps there are even better ways to do this.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文