Java 动态属性名

Java 动态属性名,java,json,jackson,Java,Json,Jackson,我想序列化一个对象,这样其中一个字段将根据字段的类型以不同的方式命名。例如: public class Response { private Status status; private String error; private Object data; [ getters, setters ] } public static class Response { private Status status; private Str

我想序列化一个对象,这样其中一个字段将根据字段的类型以不同的方式命名。例如:

public class Response {
    private Status status;
    private String error;
    private Object data;
        [ getters, setters ]
    }
public static class Response {
    private Status status;
    private String error;
    @JsonTypeInfo(include = As.WRAPPER_OBJECT, use = Id.CLASS)
    private Object data;
}
在这里,我希望将字段
data
序列化为类似
data.getClass.getName()
的内容,而不是总是有一个名为
data
的字段,该字段根据情况包含不同的类型

我如何使用Jackson实现这样的技巧?

使用自定义

第一个将打印:

{"status":"Error","error":"Some error","p":{"java.lang.Integer":20}}
第二个:

{"status":"Error","error":"Some error","p":{"java.lang.String":"some string"}}

我已经为包装器对象使用了名称
p
,因为它将仅用作
p
laceholder。如果要删除它,您必须为整个类编写一个自定义序列化程序,即
JsonSerializer

您可以使用注释
JsonTypeInfo
,它确切地告诉Jackson这一点,您不需要编写自定义序列化程序。有多种方法可以包含这些信息,但是对于您的特定问题,您可以使用
作为.WRAPPER\u对象
Id.CLASS
。例如:

public class Response {
    private Status status;
    private String error;
    private Object data;
        [ getters, setters ]
    }
public static class Response {
    private Status status;
    private String error;
    @JsonTypeInfo(include = As.WRAPPER_OBJECT, use = Id.CLASS)
    private Object data;
}
但是,这对基本类型(如字符串或整数)不起作用。对于原语,您无论如何都不需要这些信息,因为它们本机是用JSON表示的,Jackson知道如何处理它们。使用注释的额外好处是,如果需要,您可以免费获得反序列化。下面是一个例子:

public static void main(String[] args) throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    Response r1 = new Response("Status", "An error", "some data");
    Response r2 = new Response("Status", "An error", 10);
    Response r3 = new Response("Status", "An error", new MyClass("data"));
    System.out.println(mapper.writeValueAsString(r1));
    System.out.println(mapper.writeValueAsString(r2));
    System.out.println(mapper.writeValueAsString(r3));
}

@JsonAutoDetect(fieldVisibility=Visibility.ANY)
public static class MyClass{
    private String data;
    public MyClass(String data) {
        this.data = data;
    }
}
结果是:

{"status":"Status","error":"An error","data":"some data"}
{"status":"Status","error":"An error","data":10}
{"status":"Status","error":"An error","data":{"some.package.MyClass":{"data":"data"}}}
我自己的解决办法

@Data
@EqualsAndHashCode
@ToString
@JsonSerialize(using = ElementsListBean.CustomSerializer.class)
public class ElementsListBean<T> {

    public ElementsListBean()
    {
    }

    public ElementsListBean(final String fieldName, final List<T> elements)
    {
        this.fieldName = fieldName;
        this.elements = elements;
    }

    private String fieldName;

    private List<T> elements;

    public int length()
    {
        return (this.elements != null) ? this.elements.size() : 0;
    }

    private static class CustomSerializer extends JsonSerializer<Object> {
        public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException,
                JsonProcessingException
        {
            if (value instanceof ElementsListBean) {
                final ElementsListBean<?> o = (ElementsListBean<?>) value;
                jgen.writeStartObject();
                jgen.writeArrayFieldStart(o.getFieldName());
                for (Object e : o.getElements()) {
                    jgen.writeObject(e);
                }
                jgen.writeEndArray();
                jgen.writeNumberField("length", o.length());
                jgen.writeEndObject();
            }
        }
    }
}
@数据
@EqualsAndHashCode
@托斯特林
@JsonSerialize(使用=ElementsListBean.CustomSerializer.class)
公共类ElementsListBean{
公共元素slistbean()
{
}
public ElementsListBean(最终字符串字段名,最终列表元素)
{
this.fieldName=字段名;
这个元素=元素;
}
私有字符串字段名;
私有列表元素;
公共整数长度()
{
返回(this.elements!=null)?this.elements.size():0;
}
私有静态类CustomSerializer扩展JsonSerializer{
public void serialize(对象值、JsonGenerator jgen、SerializerProvider提供程序)引发IOException,
JsonProcessingException
{
if(ElementsListBean的值实例){
最终的ElementsListBean o=(ElementsListBean)值;
jgen.writeStartObject();
jgen.writeArrayFieldStart(o.getFieldName());
对于(对象e:o.getElements()){
jgen.writeObject(e);
}
jgen.writeEndArray();
jgen.writeNumberField(“长度”,o.length());
jgen.writeEndObject();
}
}
}
}

我有一个使用
@JsonAnyGetter
注释的更简单的解决方案,它工作起来很有魅力

import java.util.Collections;
import java.util.Map;

public class Response {
    private Status status;
    private String error;

    @JsonIgnore
    private Object data;

    [getters, setters]

    @JsonAnyGetter
    public Map<String, Object> any() {
        //add the custom name here
        //use full HashMap if you need more than one property
        return Collections.singletonMap(data.getClass().getName(), data);
    }
}
import java.util.Collections;
导入java.util.Map;
公众课堂反应{
私人身份;
私有字符串错误;
@杰索尼奥雷
私有对象数据;
[接球手,接球手]
@JsonAnyGetter
公共地图任意(){
//在此处添加自定义名称
//如果需要多个属性,请使用完整HashMap
返回Collections.singletonMap(data.getClass().getName(),data);
}
}

不需要包装,不需要自定义序列化程序。

注意,这使用了Lombok(projectlombok.org)的注释您好,我与OP有相同的问题。我尝试了您的解决方案,但得到的结果是:{“status”:“Error”,“Error”:“Some Error”,“p”:20}。因此@JsonProperty注释正在工作,但CustomSerializer没有工作。我使用的是Wildfly 10,并添加了RESTEasy jackson提供程序作为提供的依赖项。我也尝试了这篇文章中的建议:,但仍然没有发挥出魅力;特别是当你无法访问Serializer时有人能告诉我这是如何工作的吗?主要是any()方法如何知道我们想要重命名数据属性。方法的名称是不相关的,重要的是该方法返回一个映射,不接受任何参数,并且只有一个方法应该用这样的参数进行注释。请参阅文档地图可以包含任意数量的属性。在我的示例中,我只需要一个属性,因此,使用Singleton映射如何将json反序列化回来?例如,在我的场景中,我有一个属性Map。我为该属性指定了动态名称,序列化完全按照预期进行。现在我想将json反序列化回对象,但即使我创建了JsonAnySetter,它也不起作用。有什么建议吗?@nareshgoty您使用的
@JsonAnySetter
这里有一个很好的例子: