我正在使用Jackson序列化我的JavaPOJO类。除了字段之外,我JavaPOJO,我想在JSON中添加一些附加信息我正在编写自己的自定义CustomClassSerializer
。如果我使用这个类并注册到ObjectMapper
,那么我会得到错误:
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Type id handling not implemented for type org.acme.Tiger (by serializer of type org.acme.CustomModule$CustomClassSerializer)
我无法理解这里可能出了什么问题。如果我删除自定义注册模型,那么一切都可以完美运行。
有人能让我知道这个问题的可能原因吗?我目前使用的是Jackson 2.13.2最新版本依赖项:jackson-core, jackson-datind, jackson-注释, jackson-datatype-jdk8
:
以下是示例代码:
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.*;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "type")
@JsonSubTypes({@JsonSubTypes.Type(value = Bat.class, name = "Bat"),
@JsonSubTypes.Type(value = Tiger.class, name = "Tiger")})
@JsonInclude(JsonInclude.Include.NON_NULL)
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Animal {
private String type;
private String name;
}
@JsonInclude(JsonInclude.Include.NON_NULL)
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonTypeName("Tiger")
public class Tiger extends Animal {
private String livingType;
private String foundIn;
}
@JsonInclude(JsonInclude.Include.NON_NULL)
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonTypeName("Bat")
public class Bat extends Animal{
private String livingType;
private String foundIn;
}
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerFactory;
import java.io.IOException;
public class CustomModule extends SimpleModule {
public CustomModule() {
addSerializer(Tiger.class, new CustomClassSerializer());
}
private static class CustomClassSerializer extends JsonSerializer {
@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeStartObject();
jgen.writeObjectField("my_extra_field1", "some data");
jgen.writeObjectField("my_extra_field2", "some more data");
JavaType javaType = provider.constructType(Tiger.class);
BeanDescription beanDesc = provider.getConfig().introspect(javaType);
JsonSerializer<Object> serializer = BeanSerializerFactory.instance.createSerializer(provider, javaType);
serializer.unwrappingSerializer(null).serialize(value, jgen, provider);
jgen.writeEndObject();
}
}
}
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class TestMain {
public static void main(String[] args) throws JsonProcessingException {
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.registerModule(new CustomModule());
Tiger tiger = new Tiger();
tiger.setType("Tiger");
tiger.setName("Shera");
tiger.setFoundIn("Ground");
tiger.setLivingType("Tree");
System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(tiger));
}
}
我想知道上述例外情况的原因是什么。
作为一个面向Gson的人,我不认为我能写出真正高效的Jackson代码,但我想你可以结合以下Q/a来解决你的问题:
此外,我还想通过使用策略设计模式(如有必要)重构您的代码(很多),使其具有灵活性,并演示如何使用简单的单元测试。
public final class InterceptingSerializerModifier<T>
extends BeanSerializerModifier {
public interface IAppender<T> {
void append(JsonGenerator generator, T value)
throws IOException;
}
private final Class<T> baseClass;
private final IAppender<? super T> appender;
private InterceptingSerializerModifier(final Class<T> baseClass, final IAppender<? super T> appender) {
this.baseClass = baseClass;
this.appender = appender;
}
public static <T> BeanSerializerModifier create(final Class<T> baseClass, final IAppender<? super T> appender) {
return new InterceptingSerializerModifier<>(baseClass, appender);
}
@Override
public JsonSerializer<?> modifySerializer(final SerializationConfig config, final BeanDescription description, final JsonSerializer<?> serializer) {
if ( !baseClass.isAssignableFrom(description.getBeanClass()) ) {
return serializer;
}
@SuppressWarnings("unchecked")
final JsonSerializer<? super T> castSerializer = (JsonSerializer<? super T>) serializer;
return InterceptingJsonSerializer.create(castSerializer, appender);
}
private static final class InterceptingJsonSerializer<T>
extends JsonSerializer<T> {
private static final NameTransformer identityNameTransformer = new NameTransformer() {
@Override
public String transform(final String name) {
return name;
}
@Override
public String reverse(final String transformed) {
return transformed;
}
};
private final JsonSerializer<? super T> unwrappedSerializer;
private final IAppender<? super T> appender;
private InterceptingJsonSerializer(final JsonSerializer<? super T> unwrappedSerializer, final IAppender<? super T> appender) {
this.unwrappedSerializer = unwrappedSerializer;
this.appender = appender;
}
private static <T> JsonSerializer<T> create(final JsonSerializer<? super T> serializer, final IAppender<? super T> appender) {
return new InterceptingJsonSerializer<>(serializer.unwrappingSerializer(identityNameTransformer), appender);
}
@Override
public void serializeWithType(final T value, final JsonGenerator generator, final SerializerProvider provider, final TypeSerializer serializer)
throws IOException {
serializer.writeTypePrefix(generator, serializer.typeId(value, JsonToken.START_OBJECT));
doSerialize(value, generator, provider);
serializer.writeTypeSuffix(generator, serializer.typeId(value, JsonToken.START_OBJECT));
}
@Override
public void serialize(final T value, final JsonGenerator generator, final SerializerProvider provider)
throws IOException {
generator.writeStartObject();
doSerialize(value, generator, provider);
generator.writeEndObject();
}
private void doSerialize(final T value, final JsonGenerator generator, final SerializerProvider provider)
throws IOException {
unwrappedSerializer.serialize(value, generator, provider);
appender.append(generator, value);
}
}
}
public final class InterceptingSerializerModifierTest {
private static final BeanSerializerModifier unit = InterceptingSerializerModifier.create(
IValue.class,
(generator, value) -> {
if ( value instanceof Foo foo ) {
generator.writeObjectField("@extra.message", "this is from foo: " + foo.value);
} else if ( value instanceof Bar bar ) {
generator.writeObjectField("@extra.message", "this is from bar: " + bar.value);
} else {
generator.writeObjectField("@extra.message", "something else...");
}
}
);
private static final ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new SimpleModule()
.setSerializerModifier(unit)
);
private static Stream<Arguments> test() {
return Stream.of(
Arguments.of(
"{\"@type\":\"FOO\",\"value\":1,\"@extra.message\":\"this is from foo: 1\"}",
new Foo(1)
),
Arguments.of(
"{\"@type\":\"BAR\",\"value\":2.0,\"@extra.message\":\"this is from bar: 2.0\"}",
new Bar(2)
)
);
}
@ParameterizedTest
@MethodSource
public void test(final String expectedJson, final IValue actualValue)
throws JsonProcessingException {
Assertions.assertEquals(expectedJson, objectMapper.writeValueAsString(actualValue));
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
@JsonSubTypes({
@JsonSubTypes.Type(value = Foo.class, name = "FOO"),
@JsonSubTypes.Type(value = Bar.class, name = "BAR")
})
private sealed interface IValue
permits Foo, Bar {
}
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
private static final class Foo
implements IValue {
@Getter
private final int value;
}
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
private static final class Bar
implements IValue {
@Getter
private final double value;
}
}
我希望上面的代码是非常自我解释的。如果您需要使用额外的属性名称转换,您可能还需要增强代码以使标识符Transex
也可注入。
与主题无关,但在您的问题中重复JSON对象属性名称,如my_extra_field
,可能被认为是不推荐的(不确定是否易受攻击)。请参阅更多:JSON语法是否允许对象中存在重复键。
当我尝试反序列化汽车类时,我得到了下面的错误。杰克逊正试图在父类中的子元素中搜索字段。我如何确保杰克逊使用适当的子类型进行反序列化?我相信我需要使用混合/客户转换器。但我不确定如何在这个特定场景中使用它们。 注意:在我的例子中,除TestMain之外的所有类都在一个jar文件中,我不能修改源文件。 错误 线程"main"中的异常com.fasterxml.jackson.databind.exc.
我遇到了反序列化问题: 这是我的班级: 我要反序列化的JSON是: 我得到以下例外: 我不想补充: 因为我想得到ResObj... 如果我添加注释,它会通过,但会将其设置为null。。这是我不想要的。
我尝试使用一些类似于以下内容的JSON(来自AlphaVantage): 并使用Jackson解析它( 我的股票类如下所示: 相反,我得到了以下错误: 为什么Jackson在连接到我的股票类时遇到问题?如何将JSON中的符号连接到Stock类中的符号? 编辑:如果我将符号更改为小写,我会收到相同的错误消息:
问题内容: 我有这个JSON反序列化: 我想将其序列化为2种不同的格式: [一个] [B] 我能够将其序列化为1种格式:仅[A]或[B]。这是将其序列化为[B]的代码: 我在这里了解到http://www.baeldung.com/jackson-json-view- annotation(“5 。自定义JSON视图”部分),但它只会更改其值。我想像上面的例子一样更改字段名称。谁能对此提供见解?
问题内容: 我已经更新了我的依赖性,就像您在评论中所说的那样,现在我有了这个: 这是我的新pom: 我必须精确地说,我从未使用过杰克逊,而且运行非常正常。也许是因为auf spring 5? 问题答案: 尝试使用最新的。我将其升级到了,现在可以使用了。
问题内容: 我使用杰克逊将JSON转换为Object类。 JSON: 对象类别: 码: 我的代码抛出这样的异常: 而且我不想在Test类上添加一个道具,我只是想让jackson转换Test中也存在的存在值。 问题答案: Jackson提供了几种不同的机制来配置“额外” JSON元素的处理。以下是将to 配置为not 的示例。 有关其他方法,请参见http://wiki.fasterxml.com/