Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to change active JsonView on submodels #78

Open
mikesir87 opened this issue Jan 5, 2016 · 10 comments
Open

Ability to change active JsonView on submodels #78

mikesir87 opened this issue Jan 5, 2016 · 10 comments

Comments

@mikesir87
Copy link

I have two very simplified model objects here...

class User {
  @JsonView({ Views.Detail.class, Views.Summary.class })
  String name;

  @JsonView({ Views.Detail.class })
  Set<Post> posts = new HashSet<>();
}

and

class Post {
  @JsonView({ Views.Detail.class, Views.Summary.class })
  Date publishDate;

  @JsonView({ Views.Detail.class, Views.Summary.class })
  String title;

  @JsonView({ Views.Detail.class })
  Set<Comment> comments = new HashSet<>();
}

I have a JAXRS endpoint annotated with @JsonView, so serialization is being handled outside of my direct control. I would like to serialize User at the Detail view, but have its collections (in this case, posts) serialize using the Summary view.

I looked at making my own JsonSerializer, but looking at the SerializerProvider, I haven't found a way to change the active view and I'd hate to have to make another view class. Any pointers? Or any other way to think about this?

(ps - where's a good place to post questions like this? I don't really want to clutter the Issues queue unless it requires new code. I posted on StackOverflow but saw nothing there)

@cowtowncoder
Copy link
Member

Ok, first of all, I think the best forum would be Jackson Users list at:

https://groups.google.com/forum/#!forum/jackson-user

as to functionality to allow referring to nested children (if I understood this correctly)... That is tricky. About the only way to possibly allow this, currently, would be to construct View hierarchy using interfaces, and trying to create combinations that will work the way you want. So you would sort of combine two sets of aspects into single view interface value.

View system is not designed to be very extensible so it is not certain that it could be extended to allow more flexible handling; partly wrt view processing itself, and partly because of the way Jackson serializers (and deserializers) handle delegation: only immediate next level (de)serializer is known, and that is typically fetched statically only based on type information, and not runtime setup (like active view).
But if you could show a hypothetical annotations and/or settings that would make sense to you, we can definitely brainstorm to see possible extension routes.

Otherwise you might want to investigate JSON Filters, explained to some degree at:

http://www.cowtowncoder.com/blog/archives/2011/09/entry_461.html

They are bit more powerful and could perhaps work for your case. JAX-RS does put additional constraints of course, so I can't say how likely it is to work for you.

@mikesir87
Copy link
Author

Thanks for the information. As you mentioned, I'm also having a little trouble trying to imagine what a set of annotations might look like for this too. Maybe something like this...

class User {
  @JsonView({ Views.Detail.class, Views.Summary.class })
  String name;

  @JsonView({ Views.Detail.class })
  @JsonUseView({ Views.Summary.class })
  Set<Post> posts = new HashSet<>();
}

or something like that (I'm not stuck to that name). Basically, one annotation to say "I want to include this property when the current view is Detail", but another one to say, "Once you dive into this property, change the active view to Summary."

I was looking at the wiki page and saw that with the ObjectMapper, you can set the view that should be used. In a custom JsonSerializer, I see that the SerializerProvider gives you the current view. What if there was a way to swap the active view there?

I recognize it might be easier said than done because I'd want to only change the view for that one model (and down its tree), but retain the original view when navigating to the next property in the original object. But, I'd be fine with having to set the active view, write the collection, and set the active view back.

I guess what I'm not sure of is how the JsonGenerator and SerializerProvider are related. Would affecting the active view on the provider adjust anything on the JsonGenerator in my serializer?

@cowtowncoder
Copy link
Member

@mikesir87 yes, the view would need to be restored lexically (that is, for siblings, siblings of parent properties); and there is the question of whether semantics would get strange. But perhaps this could be done; at least there isn't anything immediately blocking that I can see.

Another more advanced possibility would allow mapping specific active view into another one, to further divide things. Not sure if that would be useful, but seems like an obvious extension.

@chrisregnier
Copy link

I've just stumbled across a very similar problem, but I ruled out using views exactly because there'd be oddities with trying to change the view dynamically. Plus specifying a static tree of views from the top level really couples code together too much.
What I was thinking might work better is to change JsonView to take a map of views to serializers. Then you can treat a property slightly different for each view.

class MyObj {
  @JsonView(view=Public.class, serialize=@JsonSerialize(using=MyPublicSerializer.class))
  @JsonView(view=Internal.class, serialize=@JsonSerialize(converter=FooToBar.class))
  public Foo foo;
}

with the ability to merge multiple views on the same property #38 I think this gives a powerful way to specify how you want a property to be treated different based on view. (I'm reusing the JsonSerialize annotation so you can specify all sorts of different ways to serialize the data. As well, I'm sure there'd be a deserialize property too.)

In my case I'm trying to serialize the inner property differently based on view, which I'd set on my JAX-RS endpoint. I believe this covers the intent of what's being requested here; although this doesn't quite give the ability to change the views dynamically, which I don't think is the right direction.

@chrisregnier
Copy link

Thinking about things further, if you add an innerView property to the JsonView I proposed before, then I think you could statically change the view for all child properties at that point, but it would have to apply to all further properties in that parsed bean tree.

class MyObj {
  @JsonView(view=Public.class, innerView=Detailed.class)
  @JsonView(view=Internal.class, serialize=@JsonSerialize(converter=FooToBar.class))
  public Foo foo;

  @JsonView(view=Detailed.class)
  public String details;

  public String name;
}

class Foo {
  public String name;
  @JsonView(view=Public.class)
  public String simple;
  @JsonView(view=Detailed.class)
  public String details;
  @JsonView(view=Detailed.class)
  public MyObj quirk;
}

Serializing MyObj normally would give:

"myObj" {
  "name"
}

Serializing MyObj with view Public should give something like this:

"myObj": {
  "foo": {
    "name": "foo name",
    "details": "foo details",
    "quirk": {
      "details": "myObj details",
      "name": "myObj name"
    }
  }
}

I could see that working, and the general use case is basically when you want to remap a view of a property because the views in the subtree are inconsistent with how things should be serialized from the top down. Of course I can see the ability to do circular refs, as I've put into my example, kinda odd, but could be totally relevant.

@chrylis
Copy link

chrylis commented May 4, 2017

I think this is what I'm looking for here. Basically, I want to be able to use my "summary" view when serializing an embedded referenced object, and the @JsonUseView model is the functionality I am trying to achieve.

@cowtowncoder
Copy link
Member

@chrylis please file a new issue unless this is directly related to feature being discussed: there is danger of getting omni-issues with multiple possibly-but-not-necessarily related aspects.
Also, it's good practice to add a brief of summary what you mean since StackOverflow is a separate system and it's annoying having to click back and forth between SO questions and issues here.

@chrylis
Copy link

chrylis commented May 8, 2017

I included the SO link merely as background context for an extended "me, too!"--my feature request is to be able to specify on a field/property the view that should be used to serialize that property, which I understand to be the request here.

@markwoon
Copy link

markwoon commented Jul 9, 2018

@cowtowncoder Is this on the roadmap? I'd like to see something like this as well. Anything that would allow a parent object to control how it's children are serialized.

Using JsonView would be nice, or even specifying a mixin to apply to child objects.

@cowtowncoder
Copy link
Member

I don't have anything specific planned at this point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants