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

Deserialise from Object (using Creator methods) returns field name instead of value #1853

Closed
askvortcov opened this issue Dec 6, 2017 · 7 comments
Milestone

Comments

@askvortcov
Copy link

askvortcov commented Dec 6, 2017

Demonstrated by the following test:

public class JacksonTest {

    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class Product {

        private final String name;

        @JsonCreator(mode = PROPERTIES)
        public Product(
            @JsonProperty("name") String name
        ) {
            this.name = name;
        }

        @JsonValue
        public String getName(){
            return name;
        }

        @Override
        public String toString() {
            return "|~" + name + "~|";
        }

        @JsonCreator(mode = DELEGATING)
        public static Product from(String name){
            return new Product(name);
        }
    }


    private static final String EXAMPLE_DATA = "{\"name\":\"dummy\",\"other\":{},\"errors\":{}}";
    private static final String TEST_PRODUCT_JSON = "\"testProduct\"";

    private ObjectMapper objectMapper = new ObjectMapper();

    @Test
    public void serialization() throws Exception {
        assertEquals(TEST_PRODUCT_JSON, objectMapper.writeValueAsString(new Product("testProduct")));
    }
    @Test
    public void deserializationFromObject() throws Exception {
        assertEquals("dummy", objectMapper.readValue(EXAMPLE_DATA, Product.class).getName());
    }
    @Test
    public void deserializationFromString() throws Exception {
        assertEquals("testProduct", objectMapper.readValue(TEST_PRODUCT_JSON, Product.class).getName());
    }
}

Which is green with 2.9.1 and fails with 2.9.2:

org.junit.ComparisonFailure: 
Expected :dummy
Actual   :name
@cowtowncoder
Copy link
Member

One quick note: usage of @JsonCreator for both constructor and static factory is probably not a good idea: only one of them will ever be selected. I forget which one should be picked up (need to go check code), but there is no dynamic determination.

But it sounds like there is something else going on here as well; thank you for reporting the problem.

@askvortcov
Copy link
Author

askvortcov commented Dec 6, 2017

Thank you for the comment. After I've removed @JsonCreator on constructor and put another one into DEFAULT mode, test started to work on both 2.9.1 and 2.9.2 versions.

// @JsonCreator -- removed
public Product(String name) { // UPD: wrong. @JsonProperty should remain 
    this.name = name;
}

@JsonCreator(mode = DEFAULT)
public static Product from(String name){
    return new Product(name);
}

UPD: In default mode the @JsonProperty("name") should remain on the constructor or on the factory method for the test to work. (v2.9.2)

// @JsonCreator -- removed
public Product(@JsonProperty("name") String name) {
    this.name = name;
}

@JsonCreator(mode = DEFAULT)
public static Product from(String name){
    return new Product(name);
}

@cowtowncoder
Copy link
Member

Ok, I hope to have time to figure out what gives wrt error reporting / reading of field name. That seems wrong at any rate. So I'll keep this open for a bit still.

@cowtowncoder
Copy link
Member

I can reproduce the problem with example. Looks like "property-based" creator is not discovered (or not retained), and only delegating one remains. Further, StringDeserializer unintentionally accepts FIELD_NAME as scalar without checks.

I will add a check for latter, which will help avoid odd error like this (because debugging problems if there were more properties would be quite confusing). And then hopefully figure out why properties-based creator is not retained, and if it can be made to work.
I think this should be possible esp. when one is factory method, other constructor.

cowtowncoder added a commit that referenced this issue Jan 17, 2018
@cowtowncoder
Copy link
Member

Interesting that part about using DEFAULT (or simply leaving mode out, which defaults to it).
That should not make any difference.

@cowtowncoder
Copy link
Member

Looks like difference has to do with explicit vs implicit typing, leading to different interpretation: in one case (working), we have "single-string" creator; in other more general "delegating".
Should detect former in all cases.

@cowtowncoder cowtowncoder changed the title Deserialise from Object returns field name instead of value from 2.9 Deserialise from Object (using Creator methods) returns field name instead of value Jan 18, 2018
@cowtowncoder cowtowncoder added this to the 2.9.4 milestone Jan 18, 2018
@cowtowncoder
Copy link
Member

Looks like handling did differ, had to add more checking in explicit case. Hope to rewrite this area in 3.0, however; code gotten complicated, brittle.

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

2 participants