有一种Spring方法可以使用JSON views过滤掉服务响应中的字段,但是缺少一种等效的方法来丰富具有某些动态/同步字段的响应;
class User{
getFirstName(){...}
getLastName(){...}
getCreateDate(){...}
}
class UserViewA{
getFullName(){
return getLastName()+", "+getFirstName()
}
getCreateDate(){...}
}
class UserViewB{
getFullName(){
return getFirstName()+" "+getLastName()
}
getCreateDate(){...}
}
我可以将用户包装在视图中,但我不想手动传播所有需要的用户字段。
我的另一个想法是使用用户对象扩展视图并创建某种引用链接器以将值引用从用户对象复制到视图,但这会使集合变得复杂。
是否有其他方法或框架来实现这一目标?这个概念根本没有得到解决吗?
更新
通过示例澄清:
我正在寻找一种外观解决方案/框架/方法。
答案 0 :(得分:9)
如何使用jackson @JsonUnwrapped
?
public class UserViewA {
@JsonUnwrapped
private User user;
public User getUser() ...
public String getFullName() {
return user.getFirstName() + " " + user.getLastName()
}
}
JsonUnwrapped只会将User的所有属性拉到根级别,并且仍然拥有UserViewA的自有属性。
答案 1 :(得分:4)
当您无法修改域对象的类时,您可以使用" 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);
}
}
最后,你需要在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}
答案 2 :(得分:1)
实际上,最好的方法是包装User对象(如果序列化框架使用getter方法)或将整个对象重写到另一个视图对象(如果序列化框架使用属性)。如果您不想手动重写对象,可以使用dozer或其他Java Bean mapping framework等框架。
如果您的模型如下所示:
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
}
答案 3 :(得分:-1)
这是怎么回事?
@Transient
@JsonProperty("fullName")
getFullName(){
return getLastName()+", "+getFirstName()
}
根据您的评论进行更新:
我建议的方法是创建一个将使用扩展或组合的UserView类(我认为在这种情况下扩展可以)。在每个附加方法(getFullName)上,您可以根据需要手动应用特定的视图策略。
如果您没有复杂的要求,可以在UserView(getFullNameA,getFullNameB)中添加所有其他方法,并使用不同的JsonView注释它们。
如果需要一个方法(getFullName)而不是两个具有不同名称的方法,则可以使用返回Arrays.asList(getFullNameA(), getFullNameB()).get(0))
的getFullName并使用不同的Json View注释注释getFullNameA()和getFullNameB()。这样,您的列表将始终包含一个项目,具体取决于所使用的视图。