英文:
How can I use @JsonView / @JsonBackReference / @JsonIdentityInfo to serialize desired child elements depending on scenario
问题
考虑使用如下的DTO结构:
@Data
class Profile {
    @JsonView({Profile.Views.Simple.class, Views.AllFields.class})
    String name;
    @JsonView(Profile.Views.AllFields.class)
    Collection<Image> images;
    static class Views {
        static class Simple {
        }
        static class AllFields {
        }
    }
}
@Data
@AllArgsConstructor
class Image {
    @JsonView({Image.Views.Simple.class, Profile.Views.AllFields.class})
    String name;
    @JsonView(Image.Views.AllFields.class)
    Profile profile;
    static class Views {
        static class Simple {
        }
        static class AllFields {
        }
    }
}
我可以想象一个相当简单的场景,根据操作的根源,我想要具有根实体的JSON输出,以及相关对象的第一级,例如:
获取用户:
{
  "name": "userName",
  "images": [
    {
      "name": "image1Name"
    }
  ]
}
或者获取图像时:
{
  "name": "imageName",
  "user": {
    "name": "userName"
  }
}
是否可以使用类似 @JsonView 的技巧,还是我必须为不同的情况创建单独的DTO?
Jackson的ObjectMapper似乎只能处理单个类的视图,因此类似以下的尝试:
Profile p = new Profile();
p.setName("ProfileName");
p.setImages(Arrays.asList(new Image("ImageName1", p), new Image("ImageName", p)));
System.out.println(mapper
    .writerWithView(Profile.Views.AllFields.class)
    .withView(Image.Views.Simple.class)
    .writeValueAsString(p));
System.out.println(mapper
    .writerWithView(Profile.Views.Simple.class)
    .withView(Image.Views.Simple.class)
    .writeValueAsString(p.getImages().iterator().next()));
结果为空的JSON。
英文:
Consider having DTOs like this
@Data
class Profile {
    @JsonView({Profile.Views.Simple.class, Views.AllFields.class})
    String name;
    @JsonView(Profile.Views.AllFields.class)
    Collection<Image> images;
    static class Views {
        static class Simple {
        }
        static class AllFields {
        }
    }
}
@Data
@AllArgsConstructor
class Image {
    @JsonView({Image.Views.Simple.class,Profile.Views.AllFields.class})
    String name;
    @JsonView(Image.Views.AllFields.class)
    Profile profile;
    static class Views {
        static class Simple {
        }
        static class AllFields {
        }
    }
}
I can imagine quite simple scenario, that depending on what is the root of the operation, I would like to have JSON output of root entities with the first level of related objects eg.
fetching Users
{
  name: userName,
  images: [
    {
      name: image1Name
    }
  ]
}
or when fetching images
{
  name: imageName,
  user: {
    name: userName
  }
}
Is is possible to use some sort of @JsonView trick, or do I have to create seprate DTOs for different scenarios?
Jackson ObjectMapper seems to be able to handle only single class view, thus attempt like
Profile p = new Profile();
p.setName("ProfileName");
p.setImages(Arrays.asList(new Image("ImageName1", p), new Image("ImageName", p)))
System.out.println(mapper
                .writerWithView(Profile.Views.AllFields.class)
                .withView(Image.Views.Simple.class)
                .writeValueAsString(p));
        System.out.println(mapper
                .writerWithView(Profile.Views.Simple.class)
                .withView(Image.Views.Simple.class)
                .writeValueAsString(p.getImages().iterator().next()));
results in empty jsons.
答案1
得分: 1
{
  "name": "ProfileName1",
  "images": [
    {
      "name": "ImageName1"
    },
    {
      "name": "ImageName2"
    }
  ]
}
{
  "name": "ImageName1",
  "profile": {
    "name": "ProfileName1"
  }
}
英文:
Jackson is kind of magic (when it works the way you want).
First problem is to solve the infinite recursion due to cyclic dependency using @JsonIdentityInfo.
Second problem is to hide specific fields while serializing respective beans using @JsonView.
Profile.java
@JsonIdentityInfo(
        generator = ObjectIdGenerators.PropertyGenerator.class,
        property = "name")
class Profile {
    String name;
    @JsonView(Views.Profile.class)
    Collection<Image> images;
    // setter getter or lombok annotations
}
Image.java
@JsonIdentityInfo(
        generator = ObjectIdGenerators.PropertyGenerator.class,
        property = "name")
class Image {
    String name;
    @JsonView(Views.Image.class)
    Profile profile;
    // constructor, setter getter or lombok annotations
}
Views.java
class Views {
    public static class Profile {}
    public static class Image {}
}
And Finally Main.java
public static void main(String[] args) throws IOException {
    Profile p1 = new Profile();
    p1.setName("ProfileName1");
    Profile p2 = new Profile();
    p2.setName("ProfileName2");
    Image image1 = new Image("ImageName1", p1);
    Image image2 = new Image("ImageName2", p2);
    p1.setImages(Arrays.asList(image1, image2));
    String json = mapper
            .writerWithView(Views.Profile.class)
            .writeValueAsString(p1);
    System.out.println(json);
    json = mapper.
            writerWithView(Views.Image.class)
            .writeValueAsString(image1);
    System.out.println(json);
}
Output:
// p1
{
  "name": "ProfileName1",
  "images": [
    {
      "name": "ImageName1"
    },
    {
      "name": "ImageName2"
    }
  ]
}
// image1
{
  "name": "ImageName1",
  "profile": {
    "name": "ProfileName1"
  }
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论