Используя jax-rs, я не уверен, как вручную разархивировать JSON в мои пользовательские объекты Java.
Из моего браузера я отправляю простой запрос пут со следующим JSON:
{"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 - это класс LocalDate Joda-time, в котором нет сеттеров для 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, и когда у меня был импорт авто-исправлений затмения, я был шокирован, увидев, что Джексон уже предоставляет расширение для 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 непосредственно наfield.
@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
Я подумываю об исключении Джексона из-за использования десериализации (хотя это довольно неплохо работает с Джерси).
Я былуже использует 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);
}