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

JsonProperty.Access.READ_ONLY not work #1805

Closed
GTxx opened this issue Oct 23, 2017 · 7 comments
Closed

JsonProperty.Access.READ_ONLY not work #1805

GTxx opened this issue Oct 23, 2017 · 7 comments
Milestone

Comments

@GTxx
Copy link

GTxx commented Oct 23, 2017

I want to disable one field deserialization, so first I add annotaion JsonProperty to the field, but it still throw exception in deserialization process.

Then I tried JsonIgnoreProperties, and annotate the class with specified field, jackson ignores the field and works as expected.

Below is the code:

// User.java
import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.Arrays;
import java.util.List;

public class User {
    private String name;

    public String getName() {
        return name;
    }

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

    @JsonProperty(access = JsonProperty.Access.READ_ONLY)
    public List<String> getRoles() {
        return Arrays.asList("admin", "monitor");
    }
}
// User1.java
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import java.util.Arrays;
import java.util.List;

@JsonIgnoreProperties(value={ "roles" }, allowGetters=true)
public class User1 {
    private String name;

    public String getName() {
        return name;
    }

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

    public List<String> getRoles() {
        return Arrays.asList("admin", "monitor");
    }
}
// AppTest.java
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;

import static org.junit.Assert.assertTrue;

public class AppTest {
    @Test
    public void test1() throws Exception {
        User user = new User();
        user.setName("foo");
        ObjectMapper objectMapper = new ObjectMapper();
        String result;
        result = objectMapper.writeValueAsString(user);
        // will throw exception
        User deserizeUser = objectMapper.readValue(result, User.class);
    }

    @Test
    public void test2() throws Exception {
        User1 user = new User1();
        user.setName("foo");
        ObjectMapper objectMapper = new ObjectMapper();
        String result = objectMapper.writeValueAsString(user);
        assertTrue(result.contains("roles"));
        // Ok
        User1 deserilizeUser = objectMapper.readValue(result, User1.class);
    }
}

The first testcase will throw exception and it's stacktrace is:

com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.UnsupportedOperationException) (through reference chain: kk.User["roles"]->java.util.Arrays$ArrayList[2])

	at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:388)
	at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:360)
	at com.fasterxml.jackson.databind.deser.std.StringCollectionDeserializer.deserialize(StringCollectionDeserializer.java:211)
	at com.fasterxml.jackson.databind.deser.std.StringCollectionDeserializer.deserialize(StringCollectionDeserializer.java:20)
	at com.fasterxml.jackson.databind.deser.impl.SetterlessProperty.deserializeAndSet(SetterlessProperty.java:130)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:276)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:140)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3814)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2858)
	at kk.AppTest.test1(AppTest.java:26)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.UnsupportedOperationException
	at java.util.AbstractList.add(AbstractList.java:148)
	at java.util.AbstractList.add(AbstractList.java:108)
	at com.fasterxml.jackson.databind.deser.std.StringCollectionDeserializer.deserialize(StringCollectionDeserializer.java:198)
	... 29 more
@wwwcomy
Copy link

wwwcomy commented Oct 23, 2017

Facing the same problem, in issue #935, seems only simple types were handled correctly.

I looked into the code, the issue was caused by some special logic for USE_GETTERS_AS_SETTERS, in BeanDeserializerFactory Line 565 (version 2.8.10):

if (propDef.hasSetter()) {
                JavaType propertyType = propDef.getSetter().getParameterType(0);
                prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType);
            } else if (propDef.hasField()) {
                JavaType propertyType = propDef.getField().getType();
                prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType);
            } else if (useGettersAsSetters && propDef.hasGetter()) {
                /* May also need to consider getters
                 * for Map/Collection properties; but with lowest precedence
                 */
                AnnotatedMethod getter = propDef.getGetter();
                // should only consider Collections and Maps, for now?
                Class<?> rawPropertyType = getter.getRawType();
                if (Collection.class.isAssignableFrom(rawPropertyType)
                        || Map.class.isAssignableFrom(rawPropertyType)) {
                    prop = constructSetterlessProperty(ctxt, beanDesc, propDef);
                }
            }

By default USE_GETTERS_AS_SETTERS is enabled, so, although the Collection member was defined Access as "READ_ONLY", still, it is set as a property in the builder instance.

My work around is using (for spring boot applications) spring.jackson.mapper.USE_GETTERS_AS_SETTERS=false

However, I'm not sure this behavior is a bug or not, @cowtowncoder please help to clarify.

@cowtowncoder
Copy link
Member

Yes, it sounds like this might be due to conflict between "getter-as-setter" (only used for Collections) and READ_ONLY modifier. I agree in that READ_ONLY should work consistently and prevent assignment, as per Javadocs.
So this sounds like a bug.

@clamothe
Copy link

clamothe commented Jan 11, 2018

Is any solution available for this, besides disabling USE_GETTERS_AS_SETTERS across the whole object mapper?

@cowtowncoder
Copy link
Member

@clamothe No progress as of yet, but I'll try to add a test to verify. I have worked on a few issues related to READ_ONLY setting, which is rather tricky to make work as expected under different usage scenarios.

@cowtowncoder
Copy link
Member

Oh. I think this is dup of #1382. Closing -- will fix both, but since that was filed earlier I'll keep that.

@cowtowncoder cowtowncoder added this to the 2.9.4 milestone Jan 24, 2018
@clamothe
Copy link

clamothe commented Jan 24, 2018

Thanks! I'll test against 2.9.4.

Update:
It works. I was able to remove my workaround and my test serialization/deserialization test still passes, which failed under 2.9.3.

Old workaround was:
@JsonIgnoreProperties(value="fieldName", allowGetters=true)

@cowtowncoder
Copy link
Member

I just released 2.9.4 so it should now be available generally (for anyone else reading).

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

4 participants