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

Allow reading/writing writerExcludingView()/readerExcludingView() (JsonView exclusion) #456

Open
astral303 opened this issue May 8, 2014 · 18 comments

Comments

@astral303
Copy link

Often times it's convenient to mark some properties for a specific purpose (e.g. "historical" or "private"), and to simply use serializers that exclude that view. This is not possible right now, as you can only include views. Right now this means that all the other properties need to be tagged with a base/common view, which clutters up the code unnecessarily (or can be forgotten sometimes).

@cowtowncoder
Copy link
Member

Sounds like a reasonable idea to me.

It seems there could be multiple ways to denote this. @JsonView could have added property like excludeIn, with Class array as value. Or, to simply change "include" into "exclude", could alternatively have a boolean property to denote that. Latter might make more sense, and be easier to implement as well.

Another approach would instead add property in @JsonIgnore, something like "inViews".

@astral303
Copy link
Author

I was thinking it's better to have ObjectMapper.writerWithoutView() and ObjectMapper.readerWithoutView(). I like how @JSONVIEW designates some metadata around the property, and then I can later make a choice whether to exclude properties of a certain type from deserialization.

However, if it's easier to do a @JSONVIEW annotation that excludes from some View, that will do the trick too.

@cowtowncoder
Copy link
Member

You can already specify 'writerWithView(null)' to remove use of active view. And specifying view value that does not match anything would allow having non-match. So how would this differ from existing possibilities?

@astral303
Copy link
Author

I'm sorry, I can see why "readerWithoutView" is confusing, and is actually a bad name (since "with"/"without" pairs are meant to control the same value).

Right now is that the list of views is an "inclusion" views list, so *erWithView method means that the object should be serialized by only including the properties that match the supplied views (plus optionally any non-view-marked properties via DEFAULT_VIEW_INCLUSION).

I'd like the ability to specify an exclusion list, where the behavior is to output non-view-marked properties, and properties belonging to any view other than the view specified. Perhaps a better method name is readerWithExcludedView.

@cowtowncoder
Copy link
Member

So you would want to inverse the logic of @JsonView annotation, such that it defines exclusion, not inclusion?

If so, this would probably be better done as a SerializationFeature, which can be changed for individual calls. And similarly for DeserializationFeature.

@WaDelma
Copy link

WaDelma commented Jul 8, 2014

@cowtowncoder So you would write something like:

mapper.writerWithView(A.class).with(EXCLUDE_VIEWS).write();

whereas @astral303 suggested:

mapper.writerWithoutView(A.class).write()

What about:

mapper.writerExcludingView(A.class).write()

@cowtowncoder
Copy link
Member

@WaDelma agree, "excludingView" sounds better, and would be more concise than what I suggested. Internally it might make sense to pass a flag of some kind (to denote reversal), but that need not be exposed to user.

@sirianni
Copy link

sirianni commented Jan 9, 2015

+1 for this feature. It would be nice to have an exclude capability with @JsonView in general. @cowtowncoder suggestion of adding an exclude attribute to the @JsonViewannotation would work.

However, I do prefer @astral303 approach of having this be defined in the object mapper config. The @JsonView should just provide metadata - the exclude/include semantics should be defined by the client/objectmapper at serialization time. With this approach you'd still need to enhance the @JsonView annotation somehow since it serves double-duty on JAX-RS methods.

@arciisine
Copy link

+1, This would have saved me quite a bit recently.

@cowtowncoder
Copy link
Member

One note on implementation: this would have to be "either or", in the sense that either active view is to be used for inclusion (include if property is included in view), or it is to be used for exclusion: it will not be possible to combine aspects. Second, internally this would need to be a combination of a SerializationFeature (and DeserializationFeature if usable on reading side too) and active view: it is possible to allow convenience method of "readerWithoutView()" / "writerWithoutView" that does both parts, but this may lead to confusion -- would "readerWithView(x)" change settings? If not, why would "readerWithoutView()" do?

So perhaps it really is better to only add Features to define inclusion/exclusion, without convenience methods, to keep things simple.

I have not had any time to work on implementing this, or even follow through to see if it works, but I think it still makes sense and should be implementable with moderate changes, not major rewrite.

@samekmichal
Copy link

Is there a workaround for this issue?

@ispringer
Copy link

+1 for this feature.

Here is my use case for it. For security reasons, I would like to mark sensitive fields (e.g. passwords) in various domain objects with @JSONVIEW(Sensitive.class), and then configure the ObjectMapper used by my Jersey server to exclude all fields with the Sensitive view from both serialization and deserialization. That way our REST API would completely hide the sensitive fields from end users.

Since there is currently no way to exclude views, I thought of a workaround. I tried making all other view classes being used in my project, except Sensitive.class, extend Public.class, and then configuring the ObjectMapper used by my Jackson server to include the Public view. Unfortunately, this did not work because there is a Jackson bug where subclass views do not work correctly (see #1146).

@greenreign
Copy link

+1 yes please

I keep getting stuck when I try to use views because it would seem they are the simplest way to filter properties of my beans. However I'm always surprised that I can't actually use a view to exclude properties which is usually what I want. Excluding password, internalIds etc. In my latest use case I want to include an internal id for one small use case and exclude it for all others. I would have to add my @JSONVIEW(PublicView.class) in 100's of places. Not the to mention each time a developer forgets to add the annotation.

Is there a workaround that folks are using? Filters seem unnecessarily complicated.

@cowtowncoder
Copy link
Member

@greenreign I do not understand the comment I can't actually use a view to exclude properties -- you definitely can. By default, un-annotated properties are included (although this is configurable setting), and you include view definition for things that are only to be included in specific view. So passwords would be included in specific view, which is to be explicitly enabled. Since inheritance checks are done (that is, all "base views", views matching base types of view required by property), you can usually build quite complicated view sets this way.

So maybe I am not understanding what you are saying here...?

@greenreign
Copy link

@cowtowncoder Right. I'm hoping I'm wrong but this requires that anytime I want to exclude password that I have to specify a view. Which means adding a lot of view wiring in my application. And debugging subtle problems when that wiring is missed. I would love to either

  1. Specify that a JsonView is for exclusion somehow
  2. Specify a default view to be used with every read/write of an object mapper when another view is not specified

This would allow me to simply annotate the field I want excluded (most of the time) and override the view in the use cases where I'd like to actually include the field.

@arciisine
Copy link

@cowtowncoder That can work for the simplest of cases, but in my scenario I have a public a API and a private API. I want to present one version of the model to the world, and another version of the model to the database. Relying on unannotated fields as the default visibility is problematic for me. I've had to build a WithoutViewAnnotationInspector to allow for exclusion of desired properties:

public class WithoutViewAnnotationInspector extends JacksonAnnotationIntrospector {

    private Set<Class<? extends Annotation>> views

    WithoutViewAnnotationInspector(Class<? extends Annotation>  ... views) {
        this.views = new HashSet<>(Arrays.asList(views)))
    }

    @Override
    boolean hasIgnoreMarker(AnnotatedMember m) {
        for (Class<? extends Annotation> view : this.views) {
             if (m.getAnnotation(view) != null) return true;
        }
        return super.hasIgnoreMarker(m)
    }
}

Then I just a build an object mapper with that introspector:

new ObjectMapper()
            .setAnnotationIntrospector(new WithoutViewAnnotationInspector(ExcludeAnnotation.class))

@cowtowncoder cowtowncoder changed the title Allow reading/writing "withoutView" (JsonView exclusion) Allow reading/writing writerExcludingView()/readerExcludingView() (JsonView exclusion) Oct 12, 2016
@cowtowncoder
Copy link
Member

Quick note: implementation of #507 -- ability to define default view for properties of a class -- might help with some use cases.

Still planning to see if I find time to implement this for 2.9 (that is, evaluate how difficult it'd be to implement, and whether that's feasible within timeline).

@cowtowncoder cowtowncoder removed the 2.9 label Mar 16, 2017
@drdamour
Copy link

drdamour commented Apr 3, 2019

this would be great to add some day, defaults for all fields make for a decent workaround

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

9 participants