JsonMappingException: No suitable constructor found for type — for an external object

瘦欲@ 提交于 2019-12-05 06:02:05

So if you look at all the classes from the org.springframework.data.geo, you will notice that pretty much all the classes don't have a no-arg constructor, which the default behavior of the ObjectMapper needs to deserialize POJOs from JSON.

One way to get around this with third-party APIs, is to make use of Jackson Mixins. If you look at the GeoModule, this is a module you can register with the ObjectMapper, that includes some Mixins

mapper.registerModule(new GeoModule());

If you look at the org.springframework.data.mongodb.core.geo, you will another module GeoJsonModule that has some included Mixins also. This module should take care of the GeoJsonPoint.

But the main problem for your use case, is (if you look back at the GeoModule) is that there is no Mixin for the GeoResult or GeoResults, which you need to parse the JSON.

I made a module to take care of the GeoResult but the GeoResults doesn't work at the moment.

public class GeoModuleExt extends SimpleModule {

    public GeoModuleExt() {
        super("Mixins", new Version(1, 0, 0, null));
        setMixInAnnotation(GeoResult.class, GeoResultMixin.class);
        setMixInAnnotation(GeoResults.class, GeoResultsMixin.class);
    }

    static abstract class GeoResultMixin {
        GeoResultMixin(@JsonProperty("content") Object content, 
                       @JsonProperty("distance") Distance distance) {    
        }
    }

    static abstract class GeoResultsMixin {
        GeoResultsMixin(@JsonProperty("results")List<GeoResult> results) {

        }   
    }
}

You can play around with it. I don't have time right now to work on it (hence the half-@$$ solution), but when I get some time, if you haven't figured it out, I'll see what I can do.

As a test, you can use the ObjectMapper in a standalone, to make sure it works first

public class Test {

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new GeoJsonModule());
        mapper.registerModule(new GeoModule());
        // Our custom module
        mapper.registerModule(new GeoModuleExt());

        // Create FoodTruckEntity - 
        GeoJsonPoint geoJsonPoint = new GeoJsonPoint(10, 10);
        FoodTruckEntity foodTruck = new FoodTruckEntity();
        // set all properties fro foodTruck

        // Create GeoResult
        GeoResult<FoodTruckEntity> geoResult
                = new GeoResult<FoodTruckEntity>(foodTruck, new Distance(10,
                                                 Metrics.KILOMETERS));

        // Serialize geoResult
        String geoResultString = mapper.writeValueAsString(geoResult);
        System.out.println(geoResultString);

        JavaType type = mapper.getTypeFactory().constructParametricType(
                GeoResult.class, FoodTruckEntity.class);

        // Deserialize geoResultString
        GeoResult<FoodTruckEntity> parsedGeoResult 
                = mapper.readValue(geoResultString, type);
        System.out.println(parsedGeoResult.getDistance());
        System.out.println(parsedGeoResult.getContent().getApplicant());

        // Up to this point everything is fine. It's the deserialization of
        // `GeoResults` thats a problem.

        /*
        List<GeoResult> results = new ArrayList<GeoResult>();
        results.add(geoResult);
        results.add(geoResult);
        GeoResults geoResults = new GeoResults(results);

        String resultsString = mapper.writeValueAsString(geoResults);
        System.out.println(resultsString);


        JavaType resultType = mapper.getTypeFactory().constructParametricType(
                                       GeoResults.class, FoodTruckEntity.class);

        GeoResults<FoodTruckEntity> parsedGeoResults 
                            = mapper.readValue(resultsString, resultType);
        for (GeoResult<FoodTruckEntity> gr: parsedGeoResults) {
            System.out.println(gr.getContent().getGeoJsonPoint());
        }*/
    }
}

When you get the test to work, you can register the ObjectMapper with Jersey like

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new GeoJsonModule());
mapper.registerModule(new GeoModule());
// Our custom module
mapper.registerModule(new GeoModuleExt());

ClientConfig config = new DefaultClientConfig();
config.getSingletons().add(new JacksonJsonProvider(mapper));
Client client = Client.create(config);

UPDATE

So after some playing around, I was able to get it to work with this Mixin for the GeoResults. Just update the above GeoModuleExt

static abstract class GeoResultsMixin {
    GeoResultsMixin(@JsonProperty("results") List<GeoResult> results, 
                    @JsonProperty("averageDistance") Distance averageDistance) {
    }

    @JsonProperty("results")
    abstract List<GeoResult> getContent(); 
}

It works as expected with the above test. Haven't tested yet with Jersey, but if it works with the ObjectMapper, it shouldn't be a problem with Jersey, as long as we have configured the Jackson provider to use the mapper.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!