Jax-rs 解组 json - 自定义类型

发布于 2024-11-19 16:44:11 字数 4437 浏览 2 评论 0原文

使用 jax-rs,我不确定如何手动将 JSON 解组到我的自定义 Java 对象中。

从我的浏览器中,我使用以下 JSON 发送一个简单的 put 请求:

{"myDate":{"dayOfMonth":23, "monthOfYear":7, "year":2011}}

在服务器上,我有一个 BlahResource,它使用此 JSON 并打印出 Java 对象属性:

@Component
@Scope("request")
@Path("/blah")
@Consumes("application/json")
@Produces("application/json")
public class BlahResource {

    @PUT
    public String putBlah(Blah blah) {
        System.out.println("Value: " + blah.getMyDate().getMonthOfYear() + "/" + blah.getMyDate().getDayOfMonth() + "/" + blah.getMyDate().getYear());
        return "{}";
    }
}

这是 Blah 的源代码:

public class Blah {

    private LocalDate myDate;

    public Blah()
    {
    }

    public void setMyDate(LocalDate myDate)
    {
        this.myDate = myDate;
    }

    public LocalDate getMyDate()
    {
        return myDate;
    }
}

问题是 Blah.myDate 是Joda-time LocalDate 类没有 dayOfMonth、monthOfYear 和 Year 的设置器。例如,当我运行此命令时,会引发以下异常:

Jul 10, 2011 8:40:33 AM
com.sun.jersey.spi.container.ContainerResponse mapMappableContainerException

SEVERE: The exception contained within MappableContainerException could not
 be mapped to a response, re-throwing to the HTTP container
org.codehaus.jackson.map.exc.UnrecognizedPropertyException:
 Unrecognized field "dayOfMonth"

这对我来说非常有意义。问题是我不知道如何编写某种适配器,以便每当遇到 LocalDate 类型时,我的适配器类都会用于将 JSON 转换为 LocalDate。

理想情况下,我想做这样的事情:

public class LocalDateAdapter {
    public LocalDate convert(String json)
    {
        int dayOfMonth = (Integer)SomeJsonUtility.extract("dayOfMonth");
        int year = (Integer)SomeJsonUtility.extract("year");
        int monthOfYear = (Integer)SomeJsonUtility.extract("monthOfYear");
        return new LocalDate(year, monthOfYear, dayOfMonth);
    }
} 

更新

我现在尝试了两种方法,但似乎都不起作用。

1)使用ObjectMapper

似乎我需要做的就是获取ObjectMapper 的句柄并添加一个反序列化器。所以我创建了这个提供者。令我惊讶的是,我将我的 dserializer 命名为:LocalDateDeserializer,当我进行 Eclipse 自动修复导入时,我惊讶地发现 Jackson 已经为 Joda 提供了扩展。当我启动服务器时,它会找到提供程序,但否则似乎永远不会调用此代码。

import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

import org.codehaus.jackson.Version;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ext.JodaDeserializers.LocalDateDeserializer;
import org.codehaus.jackson.map.module.SimpleModule;
import org.joda.time.LocalDate;
import org.springframework.stereotype.Component;

@Component
@Provider
public class ObjectMapperProvider implements ContextResolver<ObjectMapper> {
    @Override
    public ObjectMapper getContext(Class<?> type) {
        ObjectMapper mapper = new ObjectMapper();
        SimpleModule testModule = new SimpleModule("MyModule", new Version(1, 0, 0, null))
                     .addDeserializer(LocalDate.class, new LocalDateDeserializer());
                 mapper.registerModule(testModule);
        return mapper;
    }
}

2)我尝试的第二种方法是直接在字段上指定@JsonDeserialize注解。

@JsonDeserialize(using = CustomDateDeserializer.class)
private LocalDate myDate;

这似乎也没有被调用。

public class CustomDateDeserializer extends JsonDeserializer<LocalDate> {

    @Override
    public LocalDate deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException
    {
        return new LocalDate(2008, 2, 5);
    }
}

我不知道该怎么办。这似乎是一个非常基本的问题。

更新 2

我正在考虑放弃 Jackson 使用反序列化(尽管它与 Jersey 配合得很好)。

我已经在使用flexjson进行序列化了,看起来flexjson对于反序列化来说也同样简单。所有这些其他库都有一些抽象和不必要的复杂性。

在 Flexjson 中,我只需要实现 ObjectFactory:

class LocalDateTransformer implements ObjectFactory {

    @Override
    public Object instantiate(ObjectBinder context, Object value, Type targetType, Class targetClass)
    {
        HashMap map = (HashMap)value;
        int year = (Integer)map.get("year");
        int monthOfYear = (Integer)map.get("monthOfYear");
        int dayOfMonth = (Integer)map.get("dayOfMonth");
        return new LocalDate(year, monthOfYear, dayOfMonth);
    }
}

它看起来令人惊讶地像我最初发布的“适配器”类!我的资源方法现在变成:

@PUT
public String putBlah(String blahStr) {
    Blah blah = new JSONDeserializer<Blah>().use(LocalDate.class, new LocalDateTransformer()).deserialize(blahStr, Blah.class);
}

Using jax-rs, I'm not sure how to manually unmarshal JSON into my custom Java objects.

From my browser I'm sending a simple put request with the following JSON:

{"myDate":{"dayOfMonth":23, "monthOfYear":7, "year":2011}}

On the server I have a BlahResource which consumes this JSON and prints out the Java object properties:

@Component
@Scope("request")
@Path("/blah")
@Consumes("application/json")
@Produces("application/json")
public class BlahResource {

    @PUT
    public String putBlah(Blah blah) {
        System.out.println("Value: " + blah.getMyDate().getMonthOfYear() + "/" + blah.getMyDate().getDayOfMonth() + "/" + blah.getMyDate().getYear());
        return "{}";
    }
}

Here's the source code for Blah:

public class Blah {

    private LocalDate myDate;

    public Blah()
    {
    }

    public void setMyDate(LocalDate myDate)
    {
        this.myDate = myDate;
    }

    public LocalDate getMyDate()
    {
        return myDate;
    }
}

The problem is Blah.myDate is a Joda-time LocalDate class which does not have setters for dayOfMonth, monthOfYear, and year. So for instance, when I run this the following exception is thrown:

Jul 10, 2011 8:40:33 AM
com.sun.jersey.spi.container.ContainerResponse mapMappableContainerException

SEVERE: The exception contained within MappableContainerException could not
 be mapped to a response, re-throwing to the HTTP container
org.codehaus.jackson.map.exc.UnrecognizedPropertyException:
 Unrecognized field "dayOfMonth"

This makes perfect sense to me. The problem is I have no idea how to write some sort of adapter so that whenever the type LocalDate is encountered, my adapter class is used to convert the JSON into a LocalDate.

Ideally, I want to do something like this:

public class LocalDateAdapter {
    public LocalDate convert(String json)
    {
        int dayOfMonth = (Integer)SomeJsonUtility.extract("dayOfMonth");
        int year = (Integer)SomeJsonUtility.extract("year");
        int monthOfYear = (Integer)SomeJsonUtility.extract("monthOfYear");
        return new LocalDate(year, monthOfYear, dayOfMonth);
    }
} 

UPDATE

I've now tried two methods, neither seem to be working.

1) Using ObjectMapper

It seems all I need to do is get a handle on the ObjectMapper and add a deserializer. So I created this provider. To my surprise, I named my dserializer: LocalDateDeserializer and when I had eclipse auto-fix imports I was shocked to see that Jackson already provides an extension for Joda. When I start the server, it finds the provider, but otherwise it seems this code is never invoked.

import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

import org.codehaus.jackson.Version;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ext.JodaDeserializers.LocalDateDeserializer;
import org.codehaus.jackson.map.module.SimpleModule;
import org.joda.time.LocalDate;
import org.springframework.stereotype.Component;

@Component
@Provider
public class ObjectMapperProvider implements ContextResolver<ObjectMapper> {
    @Override
    public ObjectMapper getContext(Class<?> type) {
        ObjectMapper mapper = new ObjectMapper();
        SimpleModule testModule = new SimpleModule("MyModule", new Version(1, 0, 0, null))
                     .addDeserializer(LocalDate.class, new LocalDateDeserializer());
                 mapper.registerModule(testModule);
        return mapper;
    }
}

2) The second method I tried is to specify a @JsonDeserialize annotation directly on the field.

@JsonDeserialize(using = CustomDateDeserializer.class)
private LocalDate myDate;

This also didn't seem to be invoked.

public class CustomDateDeserializer extends JsonDeserializer<LocalDate> {

    @Override
    public LocalDate deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException
    {
        return new LocalDate(2008, 2, 5);
    }
}

I'm not sure what to do. This seems like a very basic problem.

UPDATE 2

I'm considering dropping Jackson for using deserialization (even though it works fairly well with Jersey).

I was already using flexjson for serialization, and it seems flexjson is just as simple for deserialization. All these other libraries have some much abstraction and unnecessary complexity.

In Flexjson, I just had to implement ObjectFactory:

class LocalDateTransformer implements ObjectFactory {

    @Override
    public Object instantiate(ObjectBinder context, Object value, Type targetType, Class targetClass)
    {
        HashMap map = (HashMap)value;
        int year = (Integer)map.get("year");
        int monthOfYear = (Integer)map.get("monthOfYear");
        int dayOfMonth = (Integer)map.get("dayOfMonth");
        return new LocalDate(year, monthOfYear, dayOfMonth);
    }
}

It looks surprisingly like the "adapter" class I originally posted! And my resource method now becomes:

@PUT
public String putBlah(String blahStr) {
    Blah blah = new JSONDeserializer<Blah>().use(LocalDate.class, new LocalDateTransformer()).deserialize(blahStr, Blah.class);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文