Java 如何使用Jersey将POJO序列化为查询参数

Java 如何使用Jersey将POJO序列化为查询参数,java,rest,jersey,pojo,query-parameters,Java,Rest,Jersey,Pojo,Query Parameters,我一直在为公司的不同服务创建多个小型Java RESTful客户端库。大多数情况下,我无法在服务器端更改任何内容,我需要编写与现有RESTful API交互的代码片段 上下文 据我所知,我一直在使用Jersey with来使用JSON:当我查询POJO时,我从JSON反序列化它,当我需要发送POJO时,我将其序列化到JSON体中。到目前为止,这两种代码片段一直在为我工作 查询和反序列化 序列化和发送 问题 我现在面对的是一个RESTful服务器,它不接受发送POJO的JSON主体。唯一有效的

我一直在为公司的不同服务创建多个小型Java RESTful客户端库。大多数情况下,我无法在服务器端更改任何内容,我需要编写与现有RESTful API交互的代码片段


上下文 据我所知,我一直在使用Jersey with来使用JSON:当我查询POJO时,我从JSON反序列化它,当我需要发送POJO时,我将其序列化到JSON体中。到目前为止,这两种代码片段一直在为我工作

查询和反序列化 序列化和发送
问题 我现在面对的是一个RESTful服务器,它不接受发送POJO的JSON主体。唯一有效的方法是使用查询参数

例如,如果我想发送对象

public MyClassPojo {
    public int attr1;
    public String attr2;
}

MyClassPojo pojo = new MyClassPojo();
pojo.attr1 = 42;
pojo.attr2 = "Foo bar";
我很想用JSON将其序列化:

{
    "attr1": 42,
    "attr2": "Foo bar"
}
但此特定RESTful服务器需要查询参数:

?attr1=42&attr2=Foo+bar

问题: 这有点糟糕,但我真的没有选择。。。我现在希望有一种简单的方法可以通过Jersey实现这一点:如何将对象自动序列化为查询参数,以发送到RESTful服务器?

注意:我在@Jukka回答时结束了这个问题。如果你真的像我一样在寻找发送x-www-form-Url编码数据的方法,请毫不犹豫地提及你提出的一个新问题。我马上就要开始工作了


更新 根据@Jukka的想法,我编写了以下方法:

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;
}
public多值映射到queryparams(){
最终多值Map queryParams=新形式();
最终字段[]字段=getClass().getDeclaredFields();
用于(字段:字段){
final boolean accessible=field.isAccessible();
试一试{
字段。setAccessible(true);
最终对象值=field.get(此);
if(值!=null){
最终字符串名称=field.getName();
add(name,value.toString());
}
}捕获(非法访问例外e){
记录器错误(“访问字段时出错”,e);
}最后{
字段设置可访问(可访问);
}
}
返回查询参数;
}
这是一个很好的起点,如果您确实需要查询参数,它将非常有效。在我的情况下,我感到困惑,我实际上需要一个x-www-form-urlencoded!为此,我不得不写一封短信


我的表单编码提供程序
@products(MediaType.APPLICATION\u FORM\u URLENCODED)
公共类MyFormEncodingProvider实现MessageBodyWriter{
私有静态最终字符串编码=“UTF-8”;
@凌驾
公共布尔值是可写的(类aClass,类型类型,注释[]注释,MediaType MediaType){
返回true;
}
@凌驾
public long getSize(对象对象对象、类aClass、类型类型、注释[]注释、MediaType MediaType){
返回-1;
}
@凌驾
public void writeTo(对象对象对象,类aClass,类型类型,注释[]注释,MediaType MediaType,
多值映射stringObjectMultivaluedMap,OutputStream OutputStream)引发IOException,WebApplicationException{
最终写入程序osWriter=新的OutputStreamWriter(outputStream);
最终多值Map fieldsAndValues=getFieldsAndValues(obj);
布尔值firstVal=true;
for(条目:fieldsAndValues.entrySet()){
最终列表值=entry.getValue();
if(values==null | | values.size()==0){
如果(!firstVal){
osWriter.write(“&”);
}
write(entry.getKey()+“=”);
firstVal=false;
}否则{
for(字符串值:值){
如果(!firstVal){
osWriter.write(“&”);
}
write(entry.getKey()+“=”+urlcoder.encode(值,编码));
firstVal=false;
}
}
}
osWriter.flush();
osWriter.close();
}
私有静态多值Map getFieldsAndValues(对象obj){
//查找所有可用字段
最终集合allFields=new ArrayList();
Class clazz=obj.getClass();
while(clazz!=null&&clazz!=Object.class){
Collections.addAll(allFields,clazz.getDeclaredFields());
clazz=clazz.getSuperclass();
}
//获取所有非空值
最终多值Map queryParams=新形式();
用于(字段:所有字段){
final boolean accessible=field.isAccessible();
试一试{
字段。setAccessible(true);
最终对象值=field.get(obj);
if(值!=null){
最终字符串名称=field.getName();
add(name,value.toString());
}
}捕获(非法访问例外e){
getLogger(AbstractIMSPojo.class).error(“访问字段时出错”,e);
}最后{
字段设置可访问(可访问);
}
}
返回查询参数;
}
}

我只想对您的POJO实现这种视图:

class Pojo {
    ...
   public MultiValuedMap<String,String> asQueryParams() {
       ...
   }
}
类Pojo{
...
公共多值映射asQueryParams(){
...
}
}

然后将结果传递给
WebResource.queryParams(..)

使用这种方法,每当POJO发生更改时,我都必须更新此视图,对吗?难道没有更好的方法吗。。也许写我自己的MessageBodyWriter?好吧,如果预期的查询参数改变了,那么除了改变它们之外就没什么可做的了,是吗?如果POJO发生了变化,那么我打赌你就是那个改变它的人,并且应该能够改变asQueryParams方法
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;
}
@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;
    }
}
class Pojo {
    ...
   public MultiValuedMap<String,String> asQueryParams() {
       ...
   }
}