如何使用Jersey将POJO序列化为查询参数 [英] How to serialize a POJO into query params with Jersey
问题描述
我一直在为公司的不同服务创建并创建多个小型Java RESTful客户端库.大多数时候,我无法在服务器端进行任何更改,我需要编写 Jersey 的代码段与现有的RESTful API进行交互.
I have been playing around and creating multiple small Java RESTful client libraries for different services at my company. Most of the time, I am unable to change anything on the server side and I need to write the Jersey pieces of code to interact with existing RESTful APIs.
据我所知,我一直在将Jersey与 Jackson 一起使用JSON:当我查询反序列化的POJO时从JSON中获取,并在需要发送POJO时将其序列化为JSON正文.到目前为止,这两种代码片段一直在为我完成工作...
Up to know, I have been using Jersey with Jackson to use JSON: when I query a POJO I deserialize it from JSON, and when I need to send a POJO, I serialize it into a JSON body. This two kinds of snippets have been doing the job for me up to now...
ClientResponse response = webResource
.path("/path/to/resource")
.queryParam("key", "value")
.accept(Mediatype.APPLICATION_JSON)
.get(ClientResponse.class);
// (...) Check response status code
MyClassPojo pojo = response.getEntity(MyClassPojo.class);
序列化和发送
ClientResponse response = webResource
.path("/path/to/resource")
.type(Mediatype.APPLICATION_JSON_TYPE)
.accept(Mediatype.APPLICATION_JSON)
.post(ClientResponse.class, pojo)
// (...) Check response status code
问题
我现在面对的是RESTful服务器,该服务器不接受用于发送我的POJO的JSON主体.似乎唯一可行的方法是使用查询参数.
Issue
I am now facing a RESTful server that does not accept JSON bodies for sending my POJOs. The only thing that seem to work is to use query parameters.
例如,如果我要发送对象
For instance, if I want to send the object
public MyClassPojo {
public int attr1;
public String attr2;
}
MyClassPojo pojo = new MyClassPojo();
pojo.attr1 = 42;
pojo.attr2 = "Foo bar";
我很想在JSON中序列化它:
I would have loved to serialize it in JSON:
{
"attr1": 42,
"attr2": "Foo bar"
}
但是此特定的RESTful服务器期望查询参数:
But this specific RESTful server is expecting query params:
?attr1=42&attr2=Foo+bar
问题
这有点烂,但是我真的没有选择...我现在希望有一个简单的方法可以使用Jersey实现此目的:我如何自动将对象序列化为查询参数,以发送到RESTful服务器?
Question
This kinda sucks, but I don't really have a choice... and I am now hoping that there is an easy way to achieve this with Jersey: how can I automatically serialize objects as query parameters, to send to a RESTful server?
注意:,我在@Jukka回答时关闭了此问题.不要犹豫,提到您创建的新问题,如果像我一样,您实际上正在寻找一种发送x-www-form-urlencoded数据的方法.我要使某事正常工作...
Note: I closed this question as @Jukka answered it. Don't hesitate referring to a new question you create, if like me you are actually looking for a way to send x-www-form-urlencoded data. I am about to get something working...
根据@Jukka的想法,我编写了以下方法:
Following an idea from @Jukka I wrote the following method:
public MultivaluedMap<String, String> toQueryParams() {
final MultivaluedMap<String, String> queryParams = new Form();
final Field[] fields = getClass().getDeclaredFields();
for (Field field : fields) {
final boolean accessible = field.isAccessible();
try {
field.setAccessible(true);
final Object value = field.get(this);
if (value != null) {
final String name = field.getName();
queryParams.add(name, value.toString());
}
} catch (IllegalAccessException e) {
LOGGER.error("Error accessing a field", e);
} finally {
field.setAccessible(accessible);
}
}
return queryParams;
}
这是一个很好的起点,如果您确实需要查询参数,则可以完美地工作.就我而言,我很困惑,实际上我需要一个x-www-form-urlencoded!为此,我不得不编写一个MessageBodyWriter!
This is a great starting point, and would work perfectly if you actually do need Query Params. In my case, I got confused and I actually need a x-www-form-urlencoded! For that purpose I had to write a MessageBodyWriter!
@Produces(MediaType.APPLICATION_FORM_URLENCODED)
public class MyFormEncodingProvider implements MessageBodyWriter<Object> {
private static final String ENCODING = "UTF-8";
@Override
public boolean isWriteable(Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType) {
return true;
}
@Override
public long getSize(Object obj, Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType) {
return -1;
}
@Override
public void writeTo(Object obj, Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> stringObjectMultivaluedMap, OutputStream outputStream) throws IOException, WebApplicationException {
final Writer osWriter = new OutputStreamWriter(outputStream);
final MultivaluedMap<String, String> fieldsAndValues = getFieldsAndValues(obj);
boolean firstVal = true;
for (Entry<String, List<String>> entry : fieldsAndValues.entrySet()) {
final List<String> values = entry.getValue();
if (values == null || values.size() == 0) {
if (!firstVal) {
osWriter.write("&");
}
osWriter.write(entry.getKey() + "=");
firstVal = false;
} else {
for (String value : values) {
if (!firstVal) {
osWriter.write("&");
}
osWriter.write(entry.getKey() + "=" + URLEncoder.encode(value, ENCODING));
firstVal = false;
}
}
}
osWriter.flush();
osWriter.close();
}
private static MultivaluedMap<String, String> getFieldsAndValues(Object obj) {
// Find all available fields
final Collection<Field> allFields = new ArrayList<>();
Class<?> clazz = obj.getClass();
while (clazz != null && clazz != Object.class) {
Collections.addAll(allFields, clazz.getDeclaredFields());
clazz = clazz.getSuperclass();
}
// Get all non-null values
final MultivaluedMap<String, String> queryParams = new Form();
for (Field field : allFields) {
final boolean accessible = field.isAccessible();
try {
field.setAccessible(true);
final Object value = field.get(obj);
if (value != null) {
final String name = field.getName();
queryParams.add(name, value.toString());
}
} catch (IllegalAccessException e) {
Logger.getLogger(AbstractIMSPojo.class).error("Error accessing a field", e);
} finally {
field.setAccessible(accessible);
}
}
return queryParams;
}
}
推荐答案
我只是将这种视图应用于您的POJO:
I would just implement this kind of view to your POJO:
class Pojo {
...
public MultiValuedMap<String,String> asQueryParams() {
...
}
}
并将结果传递给WebResource.queryParams(..)
.
这篇关于如何使用Jersey将POJO序列化为查询参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!