question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

RFE: Add support for static @JsonCreator factory methods in Mixin classes

See original GitHub issue

Moved over from: https://github.com/FasterXML/jackson-future-ideas/issues/10

There are many situations where creating a simple mixin for a class is not feasable and creating a (de)serializer is a lot of effort. It would be nice if it were possible to create a static factory method annotated with @JsonCreator in a mixin class.

For example, take the class below (actually ran into this situation trying to create a mixin for UsernamePasswordAuthenticationToken from Spring security when trying to persist all the authentication objects needed for Spring Security OAuth).

public class Foo {

    private String field1;
    private Integer field2;
    private Double field3;
    private Boolean field4;

    public Foo(String field1, Integer field2) {
        this.field1 = field1;
        this.field2 = field2;
        this.field4 = false;
    }

    public Foo(String field1, Integer field2, Double field3) {
        this.field1 = field1;
        this.field2 = field2;
        this.field3 = field3;
        this.field4 = true;
    }

    public void setField1(String field1) {
        this.field1 = field1;
    }

    public String getField1() {
        return this.field1;
    }

    public void setField2(String field2) {
        this.field2 = field2;
    }

    public String getField2() {
        return this.field2;
    }

    public void setField3(String field3) {
        this.field3 = field3;
    }

    public String getField3() {
        return this.field3;
    }

    public void setField4(String field4) {
        if (field4) {
            throw new IllegalArgumentException("not allowed");
        }
        this.field4 = false;
    }

    public String getField4() {
        return this.field4;
    }

}

As you can see, the first constructor always sets the value for field4 to false and the second constructor sets the value to true. In addition, trying to set the value to true using the setter results in an exception. This particular use case isn’t difficult to create a (de)serializer for but in the case of creating ones for the Spring Security OAuth classes () it is quite difficult. It would have been much easier if I could have created a factory class in the mixin to handle basic logic.

public abstract class FooMixin {

    @JsonCreator
    public static Foo factory(
            @JsonProperty("field1") String field1,
            @JsonProperty("field2") Integer field2,
            @JsonProperty("field3") Double field3,
            @JsonProperty("field4") Boolean field4) {

        if (field4 != null && field4) {
            return new Foo(field1, field2, field3);
        } else {
            return new Foo(field2, field2);
        }
    }

}

The solution wouldn’t necessarily need to be in the mixin but maybe could be a special kind of deserializer?

public class MyDeserializer extends SomeKindOfBaseDeserializer {

    @JsonCreator
    public static Foo factory(
            @JsonProperty("field1") String field1,
            @JsonProperty("field2") Integer field2,
            @JsonProperty("field3") Double field3,
            @JsonProperty("field4") Boolean field4) {

        if (field4 != null && field4) {
            return new Foo(field1, field2, field3);
        } else {
            return new Foo(field2, field2);
        }
    }

}

I hope my explanation is clear.

Issue Analytics

  • State:open
  • Created 6 years ago
  • Reactions:24
  • Comments:10 (2 by maintainers)

github_iconTop GitHub Comments

4reactions
vycommented, Jan 21, 2021

I am also bitten by this shortcoming yesterday. My use case is as follows:

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

class JacksonMixInTest {

    interface Vehicle {

        String getLicensePlate();

    }

    static class DefaultVehicle implements Vehicle {

        private final String licensePlate;

        private DefaultVehicle(String licensePlate) {
            this.licensePlate = licensePlate;
        }

        @Override
        public String getLicensePlate() {
            return licensePlate;
        }

    }

    abstract static class VehicleMixIn implements Vehicle {

        @JsonCreator
        static Vehicle create(
            @JsonProperty("licensePlate") String licensePlate) {
            return new DefaultVehicle(licensePlate);
        }

        @Override
        @JsonProperty("licensePlate")
        abstract public String getLicensePlate();

    }

    @Test
    void test_vehicle_deserialization() throws IOException {
        String json = "{\"licensePlate\": \"12-AB-CD\"}";
        ObjectMapper mapper = new ObjectMapper();
        mapper.addMixIn(Vehicle.class, VehicleMixIn.class);
        Vehicle vehicle = mapper.readValue(
            json.getBytes(StandardCharsets.UTF_8),
            Vehicle.class);
        Assertions
            .assertThat(vehicle)
            .isNotNull()
            .extracting(Vehicle::getLicensePlate)
            .isEqualTo("12-AB-CD");
    }

}

Here Vehicle and DefaultVehicle are located in a package that I don’t have control of.

2reactions
RatanRSurcommented, May 24, 2021

I also have a need for this.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Re: [jackson-user] Mixin factory methods? - The Mail Archive
Ok. For what it is worth, I think there is an RFE to add ability to use static factory methods from mix-in classes,...
Read more >
what is wrong with my @JsonCreator and MixIn annotation?
So in your case, annotation for 'createPoint()' would be added, but unless target class has matching factory method (to add annotations to), ...
Read more >
Jackson Mixins Common Uses - Grab Another Byte
Jackson mixins is a mechanism to add Jackson annotations to classes without modifying the actual class. It was created for those cases where ......
Read more >
FasterXML - Bountysource
RFE : Add support for static @JsonCreator factory methods in Mixin classes $ 0. Created 5 years ago in FasterXML/jackson-databind with 10 comments....
Read more >
Factory method design pattern in Java - GeeksforGeeks
The factory design pattern says that define an interface ( A java interface or an abstract class) for creating object and let the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found