How can I use @JsonView / @JsonBackReference / @JsonIdentityInfo to serialize desired child elements depending on scenario

huangapple go评论72阅读模式
英文:

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&lt;Image&gt; 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(&quot;ProfileName&quot;);

p.setImages(Arrays.asList(new Image(&quot;ImageName1&quot;, p), new Image(&quot;ImageName&quot;, 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 = &quot;name&quot;)
class Profile {

    String name;

    @JsonView(Views.Profile.class)
    Collection&lt;Image&gt; images;

    // setter getter or lombok annotations
}

Image.java

@JsonIdentityInfo(
        generator = ObjectIdGenerators.PropertyGenerator.class,
        property = &quot;name&quot;)
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(&quot;ProfileName1&quot;);

    Profile p2 = new Profile();
    p2.setName(&quot;ProfileName2&quot;);

    Image image1 = new Image(&quot;ImageName1&quot;, p1);
    Image image2 = new Image(&quot;ImageName2&quot;, 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
{
  &quot;name&quot;: &quot;ProfileName1&quot;,
  &quot;images&quot;: [
    {
      &quot;name&quot;: &quot;ImageName1&quot;
    },
    {
      &quot;name&quot;: &quot;ImageName2&quot;
    }
  ]
}

// image1
{
  &quot;name&quot;: &quot;ImageName1&quot;,
  &quot;profile&quot;: {
    &quot;name&quot;: &quot;ProfileName1&quot;
  }
}

huangapple
  • 本文由 发表于 2020年9月18日 00:42:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/63942609.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定