Java 向SpringJSON视图响应添加动态字段

Java 向SpringJSON视图响应添加动态字段,java,json,spring,jackson,Java,Json,Spring,Jackson,有一种Spring方法可以从服务响应中过滤出字段,但我缺少一种等效的方法,可以用一些动态/语法字段来丰富响应,如: class User{ getFirstName(){...} getLastName(){...} getCreateDate(){...} } class UserViewA{ getFullName(){ return getLastName()+", "+getFirstName() } getCreateDat

有一种Spring方法可以从服务响应中过滤出字段,但我缺少一种等效的方法,可以用一些动态/语法字段来丰富响应,如:

class User{
    getFirstName(){...}
    getLastName(){...}
    getCreateDate(){...}
}

class UserViewA{
    getFullName(){
      return getLastName()+", "+getFirstName()
    }
    getCreateDate(){...}
}

class UserViewB{
    getFullName(){
      return getFirstName()+" "+getLastName()
    }
    getCreateDate(){...}
}
我可以在视图中包装用户,但我不想手动传播所有需要的用户字段

我的另一个想法是使用用户对象扩展视图,并创建某种引用链接器,将值引用从用户对象复制到视图,但这将使集合变得复杂

是否有其他方法或框架来实现这一点?这一概念是否完全没有得到解决

更新:

举例说明:

  • 我不想包装User对象,因为我不想在不同的UserView对象中维护来自User类的相同getter方法
  • 我无法扩展该用户,因为它是从其他资源加载的域对象
  • 用户对象中不应有对不同UserView对象的引用
我正在寻找一种门面解决方案/框架/方法。

这个怎么样

@Transient
@JsonProperty("fullName")
getFullName(){
  return getLastName()+", "+getFirstName()
}
根据您的评论更新:

我建议的一种方法是创建一个UserView类,该类将使用扩展或组合(我认为在本例中扩展是可以的)。在每个附加方法(getFullName)上,您可以根据需要手动应用特定的视图策略

如果没有复杂的需求,可以在UserView中添加所有其他方法(getFullNameA、getFullNameB),并使用不同的JSONView对它们进行注释


如果需要一个方法(getFullName)而不是两个不同名称的方法,可以使用getFullName返回
Arrays.asList(getFullNameA(),getFullNameB()).get(0))
,并使用不同的Json视图注释对getFullNameA()和getFullNameB()进行注释。这样,您的列表将始终有一个项目,具体取决于所使用的视图

当无法修改域对象的类时,可以使用mix-in使用“virtual”字段来丰富JSON

例如,您可以创建一个名为
UserMixin
的类,该类隐藏
firstName
lastName
字段,并公开一个虚拟
fullName
字段:

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonAppend;

import java.util.Date;

@JsonAppend(
    prepend = true,
    props = {
            @JsonAppend.Prop(name = "fullName", value = UserFullName.class)
    })
public abstract class UserMixin
{
    @JsonIgnore
    public abstract String getFirstName();
    @JsonIgnore
    public abstract String getLastName();
    public abstract Date getCreatedDate();
}
然后,您将实现一个名为
UserFullName
的类,该类扩展了
VirtualBeanPropertyWriter
,以提供虚拟字段的值:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter;
import com.fasterxml.jackson.databind.util.Annotations;

public class UserFullName extends VirtualBeanPropertyWriter
{
    public UserFullName() {}

    public UserFullName(BeanPropertyDefinition propDef, Annotations contextAnnotations, JavaType declaredType)
    {
        super(propDef, contextAnnotations, declaredType);
    }

    @Override
    protected Object value(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception
    {
        return ((User) bean).getFirstName() + " " + ((User) bean).getLastName();
    }

    @Override
    public VirtualBeanPropertyWriter withConfig(MapperConfig<?> config, AnnotatedClass declaringClass, BeanPropertyDefinition propDef, JavaType type)
    {
        return new UserFullName(propDef, null, type);
    }
}
然后输出为:

{"fullName":"Frodo Baggins","createdDate":1485036953535}

使用jackson
@JsonUnwrapped
怎么样


JsonUnwrapped只会将用户的所有属性拉到根级别,并且其中仍然有自己的UserViewA属性。

实际上,最好的方法是包装用户对象(如果序列化框架使用getter方法)或将整个对象重写为另一个视图对象(如果序列化框架使用属性)。如果不想手动重写对象,可以使用类似于框架或其他框架的方法

如果您的模型如下所示:

public class User {
    private final String firstName;
    private final String lastName;

    // constructor and getter method
}
public class UserView {
    private final User user;

    public UserView(User user) {
        this.user = user;
    }

    public String getFullName() {
        return user.getFirstName() + " " + user.getLastName();
    }
}
您的wrap类可以如下所示:

public class User {
    private final String firstName;
    private final String lastName;

    // constructor and getter method
}
public class UserView {
    private final User user;

    public UserView(User user) {
        this.user = user;
    }

    public String getFullName() {
        return user.getFirstName() + " " + user.getLastName();
    }
}
也可以重写整个对象:

public class UserView {
    private final String fullName;

    public UserView(User user) {
        this.fullName = user.getFirstName() + " " + user.getLastName();
    }

    // getter if needed
}

这只适用于ViewA,不适用于ViewB。此外,我不想用视图特定的逻辑膨胀我的域对象。编辑:这个问题更多的是关于一个概念,而不是这个特定的用例。根据您的编辑:谢谢,但是1。包装用户会导致额外的维护。2.我无法扩展域对象。您可以使用AspectJ和ITD应用其他视图策略。除了通过ITD向域对象添加json视图功能外,还可以使用组合创建UserView(声明私有用户实例)并使用lombok将用户方法委托给用户类。这样,您就不必用用户方法污染您的UserView。请参见,因此
UserViewA
无法从
User
继承?在这种情况下,我认为AOP是你最好的选择。@kaqqao
UserViewA
可以从
User
继承,但我不能扩展/修改
User
。就是这样!只花了18个月:)