简体   繁体   English

反序列化列表 <Map<String, String> &gt;球衣1中的QueryParam

[英]Deserializing List<Map<String, String>> QueryParam in jersey 1

I'm trying to implement a method in a dropwizard resource, that will service a call from a JS frontend (that uses DataTables). 我正在尝试在dropwizard资源中实现一个方法,该方法将服务来自JS前端(使用DataTables)的调用。

The request has query parameters that look like this: 该请求的查询参数如下所示:

columns[0][data]=0&columns[0][name]=&columns[0][searchable]=false&columns[0][orderable]=false&columns[0][search][value]=&columns[0][search][regex]=false 列[0] [数据] = 0&列[0] [名称] =&列[0] [搜索] =假列[0] [订购] =假列[0] [搜索] [值] =&列[0] [搜索] [正则表达式] = FALSE

columns[1][data]=iata&columns[1][name]=iata&columns[1][searchable]=true&columns[1][orderable]=true&columns[1][search][value]=&columns[1][search][regex]=false 列[1] [数据] = IATA&列[1] [名称] = IATA&列[1] [搜索] =真及列[1] [订购] =真及列[1] [搜索] [值] =&列[1] [搜索] [正则表达式] = FALSE

The request comes from a JS frontend implemented with DataTables, and uses server-side processing. 该请求来自使用DataTables实现的JS前端,并使用服务器端处理。 Info about how datatables sends the requests here: 有关数据表如何在此处发送请求的信息:

https://datatables.net/manual/server-side https://datatables.net/manual/server-side

I'm having issues defining the data type for the above query parameters. 我在定义上述查询参数的数据类型时遇到问题。 With spring data, we can define it as: 使用spring数据,我们可以将其定义为:

List<Map<String, String>> columns

which can be wrapped in an object annotated with ModelAttribute and it will deserialize fine. 它可以包装在使用ModelAttribute注释的对象中,并且可以反序列化。

In my app I'm using an older version of dropwizard which depends on jersey 1.19. 在我的应用程序中,我使用的是旧版本的dropwizard,它依赖于jersey 1.19。 I've tried annotating it as a QueryParam, but the app fails at startup. 我尝试将其注释为QueryParam,但应用程序在启动时失败。

Method: 方法:

@Path("/mappings")
@GET
@Timed
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response getMappings(@QueryParam("columns") List<Map<String, String>> columns) {
  // processing here.
}

When I do this, I get: 当我这样做时,我得到:

ERROR [2016-11-07 14:16:13,061] com.sun.jersey.spi.inject.Errors: The following errors and warnings have been detected with resource and/or provider classes: SEVERE: Missing dependency for method public javax.ws.rs.core.Response com.ean.gds.proxy.ams.application.resource.gui.IataMappingGuiResource.getMappings(java.util.List) at parameter at index 0 WARN [2016-11-07 14:16:13,070] /: unavailable 错误[2016-11-07 14:16:13,061] com.sun.jersey.spi.inject.Errors:使用资源和/或提供程序类检测到以下错误和警告:SEVERE:缺少方法public javax的依赖项。 ws.rs.core.Response com.ean.gds.proxy.ams.application.resource.gui.IataMappingGuiResource.getMappings(java.util.List)at index at index 0 WARN [2016-11-07 14:16:13,070 ] /:不可用

My question is: do I have any option other than writing a custom deserializer for it ? 我的问题是:除了为它编写自定义反序列化器之外,我还有其他选择吗?

Note: If I grab the request with @Context, I can see that the decodedQueryParams are a MultivaluedMap, which maps String keys like "columns[0][data]" to Lists of String values, which always have a single element, that is the value. 注意:如果我使用@Context获取请求,我可以看到decodeQueryParams是一个多值映射,它将像“列[0] [数据]”这样的字符串键映射到字符串值列表,这些值始终只有一个元素,即价值。

Update: After some digging, I found the following JAX-RS specification (section 3.2) which explains why my approach isn't valid to begin with: 更新:经过一番挖掘,我发现了以下JAX-RS规范(第3.2节),它解释了为什么我的方法无效开始:

The following types are supported: 支持以下类型:

  1. Primitive Types 原始类型

  2. Types that have a constructor that accepts a single String argument. 具有接受单个String参数的构造函数的类型。

  3. Types that have a static method named valueOf with a single String argument. 具有名为valueOf且带有单个String参数的静态方法的类型。

  4. List, Set, or SortedSet where T satisfies 2 or 3 above. List,Set或SortedSet,其中T满足上面的2或3。

Source: Handling Multiple Query Parameters in Jersey 来源: 在泽西岛处理多个查询参数

So I've tried using just a List instead. 所以我尝试过只使用List。 This doesn't crash the app at startup, but when the request comes in, it deserializes into an empty list. 这不会在启动时使应用程序崩溃,但是当请求进入时,它会反序列化为空列表。 So the question remains as to what approach is correct. 所以问题仍然是什么方法是正确的。

In fact, you're using such a very different structure from all the common ones we have mapped for Rest Web Services consummation. 实际上,您使用的是与我们为Rest Web Services完善映射的所有常见结构完全不同的结构。 Also, because of this structural compliance problem, trying to use JSON to marshal/unmarshal the values won't suit, once we haven't object-based parameters being transported. 此外,由于这种结构合规性问题,一旦我们没有传输基于对象的参数,尝试使用JSON来编组/解组值将不适合。

But, we have a couple of options to "work this situation around". 但是,我们有几个选择“解决这种情况”。 Let's see: 让我们来看看:

  1. Going with the @QueryParam strategy is not possible because of two main reasons: 由于两个主要原因,不可能采用@QueryParam策略:

    • As you noticed, there are some limitations on its use regarding Collections other than Lists , Sets , etc; 正如您所注意到的,除了ListsSets等之外,它对Collections使用存在一些限制;
    • This annotation maps one (or a list) of param(s) by its(their) name(s), so you need every single parameter (separated by & ) to have the same name. 此注释按其(())名称映射一个(或列表)param(s),因此您需要每个参数(由&分隔)具有相同的名称。 It's easier when we think about a form that submits (via GET ) a list of checkboxes values: once they all have the same name property, they'll be sent in "name=value1&name=value2" format. 当我们考虑一个表单(通过GET )提交一个复选框值列表时会更容易:一旦它们都具有相同的name属性,它们将以"name=value1&name=value2"格式发送。

    So, in order to get this requirement, you'd have to make something like: 所以,为了达到这个要求,你必须做出类似的事情:

     @GET public Response getMappings(@QueryParam("columns") List<String> columns) { return Response.status(200).entity(columns).build(); } // URL to be called (with same param names): // /mappings?columns=columns[1][name]=0&columns=columns[0][searchable]=false // Result: [columns[1][name]=0, columns[0][searchable]=false] 

    You can also try creating a Custom Java Type for Param Annotations , like you see here . 您也可以尝试为Param Annotations创建自定义Java类型 ,就像您在此处看到的那样。 That would avoid encoding problems, but in my tests it didn't work for the brackets issue. 这样可以避免编码问题,但在我的测试中,它不适用于括号问题。 :( :(

  2. You can use regex along with @Path annotation defining what is going to be accepted by a String parameter. 您可以使用正则表达式和@Path注释来定义String参数将接受的内容。 Unfortunately, your URL would be composed by unvalid characteres (like the brackets [] ), which means your server is going to return a 500 error . 不幸的是,您的URL将由无效的characteres(如方括号[] )组成,这意味着您的服务器将返回500 error

    One alternative for this is if you "replace" this chars for valid ones (like underscore character, eg): 一种替代方法是,如果您将此字符“替换”为有效字符(例如下划线字符):

     /mappings/columns_1_=0&columns_1__name_= 

    This way, the solution can be applied with no worries: 这样,解决方案可以毫无后顾之忧地应用:

     @GET @Path("/{columns: .*}") public Response getMappings(@PathParam("columns") String columns) { return Response.status(200).entity(columns).build(); } // Result: columns_1_=0&columns_1__name_= 
  3. A much better way to do this is through UriInfo object, as you may have tried. 一个更好的方法是通过UriInfo对象,你可能已经尝试过。 This is simpler because there's no need to change the URL and params. 这更简单,因为不需要更改URL和params。 The object has a getQueryParameters() that returns a Map with the param values: 该对象有一个getQueryParameters() ,它返回一个带有param值的Map

     @GET public Response getMappings(@Context UriInfo uriInfo) { MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters(); // In case you want to get the whole generated string String query = uriInfo.getRequestUri().getQuery(); String output = "QueryParams: " + queryParams + "<br> Keys: " + queryParams.keySet() + "<br> Values: " + queryParams.values() + "<br> Query: " + query; return Response.status(200).entity(output).build(); } // URL: /mappings?columns[1][name]=0&columns[0][searchable]=false /* Result: * QueryParams: {columns[0][searchable]=[false], columns[1][name]=[0]} * Keys: [columns[0][searchable], columns[1][name]] * Values: [[false], [0]] * Query: columns[1][name]=0&columns[0][searchable]=false */ 

    However, you must be aware that if you follow this approach (using a Map ) you can't have duplicated keys, once the structure doesn't support it. 但是,您必须知道,如果您遵循此方法(使用Map ),一旦结构不支持,您就不能拥有重复的密钥。 That's why I include the getQuery() option where you get the whole string. 这就是为什么我在getQuery()选项中包含整个字符串的原因。

  4. A last possibility is creating a InjectableProvider , but I can't see many diffs to the getQuery() strategy (since you can split it and create your own map of values). 最后一种可能性是创建一个InjectableProvider ,但我看不到getQuery()策略的许多差异(因为你可以拆分它并创建自己的值映射)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM