使用 Jackson 和 Json-View 排除 json 中的字段

Posted

技术标签:

【中文标题】使用 Jackson 和 Json-View 排除 json 中的字段【英文标题】:Exclude fields in json Using Jackson and Json-View 【发布时间】:2017-10-18 05:55:27 【问题描述】:

我正在使用json-view 根据我的需要创建一个动态 json,这是一个很棒的库,我现在使用这个库有一段时间了。 最近我的一个用例遇到问题,让我先放我的代码

用户类

public class User 

    private String name;
    private String emailId;
    private String mobileNo;

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public String getEmailId() 
        return emailId;
    

    public void setEmailId(String emailId) 
        this.emailId = emailId;
    

    public String getMobileNo() 
        return mobileNo;
    

    public void setMobileNo(String mobileNo) 
        this.mobileNo = mobileNo;
    


ScreenInfoPojo 类

public class ScreenInfoPojo 

    private Long id;
    private String name;
    private ScreenInfoPojo parentScreen;
    private User createdBy;
    private User lastUpdatedBy;

    public Long getId() 
        return id;
    

    public void setId(Long id) 
        this.id = id;
    

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public ScreenInfoPojo getParentScreen() 
        return parentScreen;
    

    public void setParentScreen(ScreenInfoPojo parentScreen) 
        this.parentScreen = parentScreen;
    



    public User getCreatedBy() 
        return createdBy;
    

    public void setCreatedBy(User createdBy) 
        this.createdBy = createdBy;
    

    public User getLastUpdatedBy() 
        return lastUpdatedBy;
    

    public void setLastUpdatedBy(User lastUpdatedBy) 
        this.lastUpdatedBy = lastUpdatedBy;
     

运行代码

public class TestMain 
    public static void main(String[] args) throws JsonProcessingException 
        User user=new User();
        user.setName("ABC");
        user.setEmailId("dev@abc123.com");
        user.setMobileNo("123456789");

        ScreenInfoPojo screen1=new ScreenInfoPojo();
        screen1.setId(1l);
        screen1.setName("Screen1");
        screen1.setCreatedBy(user);
        screen1.setLastUpdatedBy(user);

        ScreenInfoPojo screen2=new ScreenInfoPojo();
        screen2.setId(2l);
        screen2.setName("Screen2");
        screen2.setParentScreen(Screen1);
        screen2.setCreatedBy(user);
        screen2.setLastUpdatedBy(user);

        ScreenInfoPojo screen3=new ScreenInfoPojo();
        screen3.setId(3l);
        screen3.setName("Screen3");
        screen3.setParentScreen(Screen2);
        screen3.setCreatedBy(user);
        screen3.setLastUpdatedBy(user);

        ScreenInfoPojo screen4=new ScreenInfoPojo();
        screen4.setId(4l);
        screen4.setName("Screen4");
        screen4.setParentScreen(Screen3);
        screen4.setCreatedBy(user);
        screen4.setLastUpdatedBy(user);

        List<ScreenInfoPojo> screens=new ArrayList<>();
        screens.add(screen1);
        screens.add(screen2);
        screens.add(screen3);
        screens.add(screen4);

        ObjectMapper mapper = new ObjectMapper().registerModule(new JsonViewModule());
        String json = mapper.writeValueAsString(JsonView.with(screens).onClass(ScreenInfoPojo.class, Match.match()
                .exclude("*")
                .include("id","name","createdBy.name","lastUpdatedBy.mobileNo","parentScreen.id")));
        System.out.println("json"+json);
        

结果

[
    "id": 1,
    "name": "Screen1",
    "parentScreen": null,
    "createdBy": 
        "name": "ABC"
    ,
    "lastUpdatedBy": 
        "mobileNo": "123456789"
    
, 
    "id": 2,
    "name": "Screen2",
    "parentScreen": 
        "id": 1,
        "name": "Screen1",
        "parentScreen": null,
        "createdBy": ,
        "lastUpdatedBy": 
    ,
    "createdBy": 
        "name": "ABC"
    ,
    "lastUpdatedBy": 
        "mobileNo": "123456789"
    
, 
    "id": 3,
    "name": "Screen3",
    "parentScreen": 
        "id": 2,
        "name": "Screen2",
        "parentScreen": 
            "id": 1,
            "name": "Screen1",
            "parentScreen": null,
            "createdBy": ,
            "lastUpdatedBy": 
        ,
        "createdBy": ,
        "lastUpdatedBy": 
    ,
    "createdBy": 
        "name": "ABC"
    ,
    "lastUpdatedBy": 
        "mobileNo": "123456789"
    
, 
    "id": 4,
    "name": "Screen4",
    "parentScreen": 
        "id": 3,
        "name": "Screen3",
        "parentScreen": 
            "id": 2,
            "name": "Screen2",
            "parentScreen": 
                "id": 1,
                "name": "Screen1",
                "parentScreen": null,
                "createdBy": ,
                "lastUpdatedBy": 
            ,
            "createdBy": ,
            "lastUpdatedBy": 
        ,
        "createdBy": ,
        "lastUpdatedBy": 
    ,
    "createdBy": 
        "name": "ABC"
    ,
    "lastUpdatedBy": 
        "mobileNo": "123456789"
    
]

预期结果

[
    "id": 1,
    "name": "Screen1",
    "parentScreen": null,
    "createdBy": 
        "name": "ABC"
    ,
    "lastUpdatedBy": 
        "mobileNo": "123456789"
    
, 
    "id": 2,
    "name": "Screen2",
    "parentScreen": 
        "id": 1
    ,
    "createdBy": 
        "name": "ABC"
    ,
    "lastUpdatedBy": 
        "mobileNo": "123456789"
    
, 
    "id": 3,
    "name": "Screen3",
    "parentScreen": 
        "id": 2
    ,
    "createdBy": 
        "name": "ABC"
    ,
    "lastUpdatedBy": 
        "mobileNo": "123456789"
    
, 
    "id": 4,
    "name": "Screen4",
    "parentScreen": 
        "id": 3
    ,
    "createdBy": 
        "name": "ABC"
    ,
    "lastUpdatedBy": 
        "mobileNo": "123456789"
    
]

问题

在我的用例中,我有一个类 ScreenInfoPojoparentScreen 引用相同的类, 我正在尝试获取父 ("parentScreen.id") 的特定字段/字段,因为我正在获取我在子/目标对象 ("id","name ","createdBy.name","lastUpdatedBy.mobileNo","parentScreen.id") 和父响应再次递归!我观察到的一件事只有在类有自己的引用的情况下才会发生,我将 User 类引用作为两个不同的字段 createdBylastUpdatedBy 并尝试分别获取 "name""mobileNo" 效果很好。

任何解决这个问题的建议都会非常有帮助!!!!

谢谢

【问题讨论】:

你能简单地用id属性创建另一个POJO并将它分配给parentScreen属性吗? 不,我做不到 为什么?从我的角度来看,它是一个独特的实体——类似于ScreenRefPojo 我的应用程序有多个类(pojo),并且在运行时应用了匹配,这意味着根据客户端的要求它将与属性(使用 ajax)一样,因此创建不同的类会增加额外的开销. 我看到您在 json-view 上打开了一个问题(我同意您的代码应该按预期运行),但您至少尝试排除 parentScreen.* 和/或 @987654331 @ ? 【参考方案1】:

是的。 Include 子句对同一个类的引用不起作用。

你能做到吗?

根据github指令从源码编译build from source

更新函数JsonViewSerializer.JsonWriter.fieldAllowed

找到:

        if(match == null) 
            match = this.currentMatch;
         else 
            prefix = "";
        

并注释else子句

        if(match == null) 
            match = this.currentMatch;
         else 
            //prefix = "";
        

你会得到预期的结果。但。我不知道它会如何影响其他过滤器。

要获得更多控制,您可以向 JsonView 类添加属性。

例如:

在JsonView中添加:

private boolean ignorePathIfClassRegistered = true;

     public boolean isIgnorePathIfClassRegistered() 
            return ignorePathIfClassRegistered;
        

        public JsonView1<T>  setIgnorePathIfClassRegistered(boolean ignorePathIfClassRegistered) 
            this.ignorePathIfClassRegistered = ignorePathIfClassRegistered;
            return this;
        

在 JsonViewSerializer.JsonWriter.fieldAllowed 函数中将 if 子句重写为:

        if(match == null) 
            match = this.currentMatch;
         else 
            if (result.isIgnorePathIfClassRegistered())
            prefix = "";
        

你可以在你的例子中使用它:

JsonView<List<ScreenInfoPojo>> viwevedObject = JsonView
        .with(screens)
        .onClass(ScreenInfoPojo.class,
                Match.match()
                        .exclude("*")
                        .include("id","name")
                        .include("createdBy.name")
                        .include("lastUpdatedBy.mobileNo")
                        .include("parentScreen.id"))
        .setIgnorePathIfClassRegistered(false);

ObjectMapper mapper = new ObjectMapper().registerModule(new JsonViewModule());
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);

String json = mapper.writeValueAsString(viwevedObject);

【讨论】:

感谢您的宝贵回答,实际上我已经这样做了,我不想修改库源代码,但似乎没有其他方法可以接受这项工作。感谢您花费宝贵的时间。【参考方案2】:

您可以在 json 响应中不需要的字段上简单地使用 jackson 注释 @jsonignore

我不知道您是否可以在代码上使用任何注释。如果是这样,这没用..

【讨论】:

是的,使用这种方法,他只需添加一个带有父 id 的附加字段,以仍然具有最少代码重构量的参考。看起来很简单。【参考方案3】:

序列化对象最灵活的方法是编写自定义序列化器。

如果我正确理解了您的要求,以下序列化程序可能会起作用:

public class CustomScreenInfoSerializer extends JsonSerializer<ScreenInfoPojo> 

    @Override
    public void serialize(ScreenInfoPojo value, JsonGenerator gen, SerializerProvider serializers)
            throws IOException, JsonProcessingException 
        gen.writeStartObject();
        gen.writeNumberField("id", value.getId());
        gen.writeStringField("name", value.getName());
        gen.writeFieldName("createdBy");
        gen.writeStartObject();
        gen.writeStringField("name", value.getCreatedBy().getName());
        gen.writeEndObject();
        gen.writeFieldName("lastUpdatedBy");
        gen.writeStartObject();
        gen.writeStringField("mobileNo", value.getLastUpdatedBy().getMobileNo());
        gen.writeEndObject();
        if (value.getParentScreen() == null) 
            gen.writeNullField("parentScreen");
        
        else 
            gen.writeFieldName("parentScreen");
            gen.writeStartObject();
            gen.writeNumberField("id", value.getParentScreen().getId());
            gen.writeEndObject();
        
        gen.writeEndObject();

    


使用

ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(ScreenInfoPojo.class, new CustomScreenInfoSerializer());
mapper.registerModule(module);
String json = mapper.writeValueAsString(screens);
System.out.println(json);

生产

[
   
      "id": 1,
      "name": "Screen1",
      "createdBy": 
         "name": "ABC"
      ,
      "lastUpdatedBy": 
         "mobileNo": "123456789"
      ,
      "parentScreen": null
   ,
   
      "id": 2,
      "name": "Screen2",
      "createdBy": 
         "name": "ABC"
      ,
      "lastUpdatedBy": 
         "mobileNo": "123456789"
      ,
      "parentScreen": 
         "id": 1
      
   ,
   
      "id": 3,
      "name": "Screen3",
      "createdBy": 
         "name": "ABC"
      ,
      "lastUpdatedBy": 
         "mobileNo": "123456789"
      ,
      "parentScreen": 
         "id": 2
      
   ,
   
      "id": 4,
      "name": "Screen4",
      "createdBy": 
         "name": "ABC"
      ,
      "lastUpdatedBy": 
         "mobileNo": "123456789"
      ,
      "parentScreen": 
         "id": 3
      
   
]

【讨论】:

感谢您的回答,如果我只能在一两个类中处理这个问题,我可以应用这个解决方案,但遗憾的是我有很多类(实体)面临这个问题,这个 json 属性是我的改变对于同一个 api 的不同请求,所以我不能对实体类使用自定义序列化程序,我必须使其成为动态的。

以上是关于使用 Jackson 和 Json-View 排除 json 中的字段的主要内容,如果未能解决你的问题,请参考以下文章

json-view

com.fasterxml.jackson.databind.JsonSerializer 按键排除字段

JPA / Jackson - 反序列化时排除字段并在序列化时包含它们

如何在 Jackson 中记录 JSON 反序列化

jackson 常用注解,比如忽略某些属性,驼峰和下划线互转

详解jackson注解(三)jackson包含属性、忽略属性的注解