将动态字段添加到春季 JSON 视图响应

2022-09-03 02:06:41

有一种Spring方法可以从具有JSON视图的服务响应中过滤掉字段,但是我缺少一种等效的方法,可以使用一些动态/合成字段来丰富响应,如下所示;

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

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

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

我可以将用户包装在视图中,但我不想手动传播所有需要的用户字段。

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

是否有其他方法或框架来实现这一目标?这个概念根本没有以任何方式解决吗?

更新:

通过示例进行澄清:

  • 我不想包装 User 对象,因为我不想在不同的 UserView 对象中维护来自 User 类的相同 getter 方法。
  • 我无法扩展 User,因为它是从其他资源加载的域对象。
  • 在 User 对象中不应引用不同的 UserView 对象。

我正在寻找一种外观解决方案/框架/方法。


答案 1

使用杰克逊怎么样?@JsonUnwrapped

http://fasterxml.github.io/jackson-annotations/javadoc/2.0.0/com/fasterxml/jackson/annotation/JsonUnwrapped.html

public class UserViewA {

    @JsonUnwrapped
    private User user;

    public User getUser() ...

    public String getFullName() {
        return user.getFirstName() + " " + user.getLastName()
    }
}

JsonUnwpacked只会将User的所有属性拉到根级别,并且仍然具有UserViewA的自己的属性。


答案 2

当您无法修改域对象的类时,可以使用混合使用“虚拟”字段来丰富 JSON。

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

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

然后,您将实现一个名为 的类,该类扩展以提供虚拟字段的值:UserFullNameVirtualBeanPropertyWriter

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

最后,您需要向 ObjectMapper 注册混音,如以下 JUnit 测试所示:

@Test
public void testUserFullName() throws IOException
{
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.addMixIn(User.class, UserMixin.class);
    System.out.println(objectMapper.writeValueAsString(new User("Frodo", "Baggins")));
}

然后,输出为:

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