I am new to JSON and Groovy. I can make an AJAX call to a groovlet and make it build some HTML codes with MarkupBuilder. Along with the HTML string being returned, I want a JSON string to be filled in one of a input text box. The problem is the use of JsonGroovyBuilder(). I can't even get the simplest example shown in Json-lib to run correctly. Here is my code:
import net.sf.json.groovy.*;
import net.sf.json.groovy.JsonGroovyBuilder;
def builder = new JsonGroovyBuilder()
def books = builder.books {
book = [title: "Groovy in Action", author: "Dierk Konig"]
book = [title: "Groovy in Action", author: "Dierk Konig"]
}
I run this simple piece of code on GroovyConsole and I get this in return:
Result: {"books":null}
Very odd. But the even "odder" thing is when I run it in Eclipse, I get this:
Caught: groovy.lang.MissingMethodException: No signature of method: net.sf.json.groovy.JsonGroovyBuilder.books() is applicable for argument types: (JSONSandbox$_run_closure1) values: [JSONSandbox$_run_closure1#164debb]
Possible solutions: is(java.lang.Object)
at JSONSandbox.run(JSONSandbox.groovy:6)
I think that I have all the jar files I needed:
json-lib-2.3-jdk15.jar
commons-collections-2.1.jar
commons-lang-2.3.jar
httpclient-4.0.1.jar
I have been stuck in this problem for a couple of days now. Perhaps I have done something wrong, or misunderstanding the use of this function. Everywhere I search on JSON and Groovy points back to Grails. I am still new to Groovy and I don't know Grails. I don't want to throw away my Groovy codes and start over. What could be the fix of this problem? Many many thanks in advance!
I've never tried using JsonGroovyBuilder, so can't really help you with that. I was similar frustrated by the JSON Builder provided by Grails 1.1 (which was replaced with a better version in Grails 1.2). I overcame this frustration by writing my own Groovy JSON builder, which you're welcome to use. I've pasted the source code below.
import org.json.JSONStringer
/**
* An alternative JSON builder, because <code>grails.util.JSonBuilder</code> sucks!
* The reasons why it sucks are described here: http://www.anyware.co.uk/2005/2008/06/19/a-grails-json-builder-that-doesnt-suck/
* Although this page provides an alternative JSON builder, the author didn't provide any usage examples, and I couldn't
* figure out how to use it, so I wrote my own instead.
*
* The underlying JSON functionality is provided by <code>json.org.JSONStringer</code>. An example usage is:
*
* <code>
* builder.array(['feck', 'arse', 'girls']) {
* foo(bar:'1', baz: '2') {
* hobbies(sport: 'snooker', drink: 'guinness')
* emptyObj([:])
* emptyArray([])
* }
* }
* builder.json
* </code>
*
* This will print:
* <code>
* ["feck","arse","girls", {"bar":"1", "baz":"2", "hobbies": {"sport":"snooker", "drink":"guinness"}, "emptyObj": {},"emptyArray":[]}]
* </code>
*
* Verifiable usage examples are provided by the unit tests. A few points worth noting (the term 'element' is used below
* to mean 'either a JSON object or JSON array'):
*
* <ul>
* <li>The nesting of elements is indicated by the nesting of closures in the usual Groovy builder fashion</li>
* <li>The name of the method is used as the name of the key when nesting an element within an object</li>
* <li>The name of the method is irrelevant when nesting an element within an array, but it is recommended
* to use either the method name 'object' or 'array' for the sake of code readability</li>
* <li>The decision to create an array or object is determined by the type of the method parameter. A map will cause
* an object to be created, any other type will cause an array to be created</li>
* <li>To create an empty array or an array whose contents are determined only by nested closures, either call
* <code>builder.array()</code> or <code>builder.keyName([])</code>. The latter should be used when nesting the empty
* array within an object and control over the key name is required.</li>
* <li>To create an empty object or an object whose contents are determined only by nested closures, either call
* <code>builder.object()</code> or <code>builder.keyName([:])</code>. The latter should be used when nesting the empty
* object within another object and control over the key name is required.</li>
* </ul>
*/
class SimpleJSONBuilder extends BuilderSupport {
private jsonText = new JSONStringer()
/**
* Returns the JSON text created by this builder
*/
String getJson() {
jsonText.toString()
}
String toString() {
getJson()
}
protected void setParent(Object parent, Object child) {
// Method is abstract in parent class, but an empty implementation is all we need
}
/**
* Creates an array or object which is either empty, or whose contents are determined exclusively by nested closures.
*/
protected Object createNode(Object name) {
if (current == ElementType.OBJECT) {
throw new IllegalStateException("""Error processing method $name() Empty argument methods should not be invoked
when nesting an element within an object because the key name cannot be determined. Replace this call with either
$name([]) or $name([:])""")
}
if (name == 'array') {
jsonText.array()
return ElementType.ARRAY
} else if (name == 'object') {
jsonText.object()
return ElementType.OBJECT
} else {
throw new IllegalArgumentException("""When calling a method with no arguments, the method must be named either
'$array' or '$object' to indicate which you wish to create""")
}
}
protected Object createNode(Object name, Map attributes, Object value) {
throw new UnsupportedOperationException("Error invoking method $name. Method calls must supply either a single object (to create an array) or a Map (to create an object)")
}
/**
* Ensures that an array/object is correctly nested within an object
* #name Name of the key to use for the nested element
* #return The type of element
*/
private void nestElement(name) {
if (current == ElementType.OBJECT) {
jsonText.key(name)
}
}
/**
* Creates an array
* #name Name of the method. This will be used as the key if the array is nested within an object
* #value The contents of the array. This should be either a single value or a collection or array
* #return The type of element
*/
protected Object createNode(Object name, Object value) {
nestElement(name)
jsonText.array()
if (value instanceof Collection || value instanceof Object[]) {
value.each {jsonText.value(it)}
} else {
jsonText.value(value)
}
return ElementType.ARRAY
}
/**
* Creates an object
* #name Name of the method. This will be used as the key if the object is nested within an object
* #value The name-value pairs contained by this object
* #return The type of element
*/
protected Object createNode(Object name, Map attributes) {
nestElement(name)
jsonText.object()
attributes.each {key, value ->
jsonText.key(key).value(value)
}
return ElementType.OBJECT
}
protected void nodeCompleted(Object parent, Object node) {
node == ElementType.OBJECT ? jsonText.endObject() : jsonText.endArray()
}
}
private enum ElementType {
ARRAY, OBJECT
}
The source code above defines the class SimpleJSONBuilder and the enum SimpleJSONBuilder, but you can store both of these in a single file SimpleJSONBuilder.groovy.
The only library required by this builder is the Java JSON library provided by json.org.
In case the comments in the code above don't explain how to use it sufficiently well, here are some test cases:
public class SimpleJSONBuilderTests extends GroovyTestCase {
void testRootArrayElement() {
def builder = new SimpleJSONBuilder()
builder.array(['feck', 'arse', 'girls']) {
foo(bar: '1', baz: '2') {
hobbies(sport: 'snooker', drink: 'guinness')
emptyObj([:])
emptyArray([])
}
}
assertEquals builder.json, '["feck","arse","girls",{"bar":"1","baz":"2","hobbies":{"sport":"snooker","drink":"guinness"},"emptyObj":{},"emptyArray":[]}]'
}
void testRootObjElement() {
def builder = new SimpleJSONBuilder()
builder.object(feck:'arse') {
foo(bar: '1', baz: '2') {
hobbies(sport: 'snooker', drink: 'guinness')
emptyObj([:])
emptyArray([])
}
}
assertEquals builder.json, '{"feck":"arse","foo":{"bar":"1","baz":"2","hobbies":{"sport":"snooker","drink":"guinness"},"emptyObj":{},"emptyArray":[]}}'
}
/**
* Test that both mechanisms for creating empty arrays are equivalent
*/
void testEmptyArrays() {
def builder = new SimpleJSONBuilder()
builder.array([])
def builder2 = new SimpleJSONBuilder()
builder2.array()
assertEquals builder.json, builder2.json
assertEquals builder.json, "[]"
}
/**
* Test that both mechanisms for creating empty objects are equivalent
*/
void testEmptyObjects() {
def builder = new SimpleJSONBuilder()
builder.object([:])
def builder2 = new SimpleJSONBuilder()
builder2.object()
assertEquals builder.json, builder2.json
assertEquals builder.json, "{}"
}
}
It is a minor bug in JsonGroovyBuilder. If you use it from script, then unknown properties are resolved using script's binding.
I have posted a patch for JsonBuilder, so I hope the bug will be resolved soon.
See https://sourceforge.net/tracker/?func=detail&aid=3022114&group_id=171425&atid=857928 for details.
Related
As part of my JHipster-generated Spring Boot app, I'm trying to adapt an integration test to ensure that the return JSON object contains null for a particular property:
import static org.springframework.test.web.servlet.ResultActions.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
.andExpect(jsonPath("$.selectedChannels[0].channelValue")
.doesNotExist())
This doesn't seem to work. For the response:
{
"selectedChannels": [{
"name": "test channel",
"channelValue": null
}]
}
I would get a response:
java.lang.AssertionError: Expected no value at JSON path "$.selectedChannels[0].channelValue" but found: [null]
I suppose the "doesNotExist" call is trying to make sure that the key does not exist. I tried using "value" as well with a null value but this just throws a NPE. How do I perform this assertion successfully?
The JSON Path Matcher doesNotExist() is intented to be used to assert that no key is present. For your example the key channelValue is present and therefore the assertion fails. See also official Javadocs (https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/web/servlet/result/JsonPathResultMatchers.html#doesNotExist--) for JsonPathResultMatchers doesNotExist():
Evaluate the JSON path expression against the response content and assert that a value does not exist at the given path.
What you want for your provided response is checking if the response contains a null value for the key channelValue:
resultActions.andExpect(jsonPath("$.selectedChannels[0].channelValue").value(nullValue());
Make sure to add a static import for Hamcrest nullValue() matcher: import static org.hamcrest.Matchers.nullValue;
I believe there may be some bug that causes Spring Framework (Spring Boot version 1.5.4 currently) to render the null value as a net.minidev.json.JSONArray with a single value of null. So "channelValue": null is rendered to "channelValue": [null]. This prevents the nullValue matcher from working.
Currently the only solution I could muster to this problem was to implement and use my own Matcher. Using the NullValue matcher as a template I have this:
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import net.minidev.json.JSONArray;
/**
* Matcher that can be used to detect if a value is null or refers to an empty list,
* or a list that contains a single null value.
*
* #param <T>
*/
public class IsNullOrEmpty<T> extends BaseMatcher<T> {
#Override
public boolean matches(Object item) {
System.out.println(item.getClass().getPackage());
if(item instanceof JSONArray) {
JSONArray arr = ((JSONArray)item);
return arr==null || arr.size()==0 || (arr.size()==1 && arr.get(0)==null);
}
return item==null;
}
#Override
public void describeTo(Description description) {
description.appendText("null or list of null");
}
/**
* Detect if the value is null or contains an empty array, or an array of a single element
* of null
* #return
*/
public static Matcher<Object> isNullOrEmpty() {
return new IsNullOrEmpty<>();
}
}
/**
* This is the model class for table "hashtag".
*
* #property string $text
*
* #property TweetHashtag[] $tweetHashtags
* #property Tweet[] $tweets
*/
class Hashtag extends ActiveRecord
{
.........
public function getTweetHashtags()
{
return $this->hasMany(TweetHashtag::className(), ['hashtag_text' => 'text']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getTweets()
{
return $this->hasMany(Tweet::className(), ['id' => 'tweet_id'])->viaTable('tweet_hashtag', ['hashtag_text' => 'text']);
}
}
When I do in some component
$hashtags = Hashtag::find()
->with('tweets')
->where(['text' => $hashtagText])
->all();
foreach($hashtags as $hashtag)
{
print_r($hashtag->tweets);
}
It`s working but why tweets - field accessed only via magic method and how can i fix it? And tweetHashtags working well.
Class Tweet have same relationship but public function getHashtags() working without this problem.
Your question is not clear. Each method on a Component class that start with get (like getName) can be accessed with property form (e.g. name). On special case, relations of Yii's ActiveRecord, if you access to relation by property form, you get results. In fact $this->tweets is a shorthand for $this->getTweets()->all().
P.S: On Yii2 Document http://www.yiiframework.com/doc-2.0/guide-db-active-record.html#accessing-relational-data:
Note: While this concept looks similar to the object property feature,
there is an important difference. For normal object properties the
property value is of the same type as the defining getter method. A
relation method however returns an yii\db\ActiveQuery instance, while
accessing a relation property will either return a yii\db\ActiveRecord
instance or an array of these.
$customer->orders; // is an array of `Order` objects
$customer->getOrders(); // returns an ActiveQuery instance
This is useful for creating customized queries, which is described in the next section.
I'm trying to implement a custom marshaller in Grails. Here's the marshaller:
class AdultPlanningMarshaller implements ObjectMarshaller<JSON> {
boolean supports(Object theObject)
{
return theObject instanceof AdultPlanning
}
void marshalObject(Object theObject, JSON theConverter)
{
AdultPlanning adult = (AdultPlanning)theObject
JSONWriter writer = theConverter.getWriter()
writer.object()
writer.key('id').value(adult.id)
...
writer.endObject()
}
}
I'm registering it in bootstrap.groovy and when I run my integration tests, the supports method fires correctly and the marshalObject method is called with the right object and a JSON object.
When I hit the:
writer.object()
call, an exception gets thrown:
org.codehaus.groovy.grails.web.json.JSONException: Misplaced object: expected mode of INIT, OBJECT or ARRAY but was DONE
So it looks like the writer has already done something to completion, but I have no clue what.
There's not a lot of documentation on JSON marshallers and examples are thin on the ground, but I think I've done this right but it sure isn't working. Any hints would be appreciated.
Further work with the debugger seems to indicate that the object marshaller is being called twice, although breakpoints only happen on the 2nd call for some reason. The first time through it seems to work just fine since the JSONWriter that I get via theConverter.getWriter() when the breakpoint DOES work has the JSON of the object correctly marshalled. It's the 2nd call that is blowing up since the object has ALREADY been marshalled and the JSONWriter is no longer in the "init" state. There's nothing obviously available to tell the difference between the two calls, but why it the marshaller being called twice?
As requested, here's the controller. It's the show action that's being triggered:
class PrimaryController extends RestfulController implements AlwaysRenderJsonException {
def springSecurityService
def familyService
static responseFormats = ['json']
PrimaryController() {
/*
* Tell the base class the name of the resource under management.
*/
super(Primary)
}
#Override
protected Primary createResource() {
//def instance = super.createResource()
//TODO: Should be able to run the above line but there is an issue GRAILS-10411 that prevents it.
// Code from parent is below, as soon as the jira is fixed, remove the following lines:
Primary instance = resource.newInstance()
bindData instance, this.getObjectToBind()
//Code from super ends here
def family = familyService.safeGetFamily(params.long('familyId'))
familyService.addAdultToFamily(instance, family) // Add the primary member to the family.
return instance
}
/**
* Deletes a resource for the given id
* #param id The id
*/
#Override
def delete() {
if(handleReadOnly()) {
return
}
Child instance = queryForResource(params.id)
if (instance == null) {
notFound()
return
}
/*
* Because of the multiple belongsTo relationships of events, you have to get rid of all
* the events and make the profiles consistent BEFORE deleting the person instance.
*/
instance.removePerson()
request.withFormat {
'*'{ render status: NO_CONTENT } // NO CONTENT STATUS CODE
}
}
#Override
protected List<Primary> listAllResources(Map params) {
if (params.familyId == null)
{
throw new ESPException("params.familyId may not be null")
}
def user = springSecurityService.loadCurrentUser()
return \
AdultPlanning.where {
family.id == params.familyId \
&& family.user == user \
&& typeOfPerson == PeopleTypeEnum.PRIMARY
}.list()
}
#Override
protected Primary queryForResource(Serializable id) {
def inst = familyService.safeGetAdult(Long.parseLong(id), params.long('familyId'))
/*
* It was safe to access the requested id, but the requested id may NOT be a primary
* so we need to check.
*/
return (inst instanceof Primary ? inst : null)
}
/**
* Show the primary for the specified family.
*
* #return
*/
#Override
def show() {
Primary primary = familyService.safeGetFamily(params.long('familyId'))?.primary
respond primary
}
}
And the Integration test that triggers it:
void "We should be able to show a primary."() {
given:
family.addToAdults(new Primary(firstName: "Barney"))
family.save()
family.adults.each { it.save() }
when:
controller.response.reset()
resetParameters(controller.params, [familyId: family.id])
controller.request.method = 'GET'
controller.show()
then:
1 * mSpringSecurityService.loadCurrentUser() >> user
controller.response.json
controller.response.json.firstName == "Barney"
}
Well, this is embarrassing.
I use IntelliJ as my Java/Groovy IDE. I had a work related thing to do this morning and quit IntelliJ. When I restarted IntelliJ, the problem described above that had been completely reproducible was no longer happening and the appropriate JSON was being produced under all circumstances.
So it appears that the IntelliJ state somehow got corrupted and the restart cleared it out.
Problem solved.
I guess.
Thanks for the help/suggestions.
As OP mentioned, this error can be triggered because of IntelliJ :
org.codehaus.groovy.grails.web.json.JSONException: Misplaced object: expected mode of INIT, OBJECT or ARRAY but was DONE
Indeed, when debugging the marshaller (for example), IntelliJ displays the "toString()" of the variable, which causes the change of the mode from INIT to DONE.
You may want to remove your breakpoints when facing this issue ;)
The only reason for this can be that where you have started writer.object() for some nested object or array response but missed to write writer.endObject() or you have wrote it two times.
So double check your custom marshaller for all write object.
Reference: https://github.com/grails/grails-core/blob/65b42b66821b32d4efb3a229da99691a00575d60/grails-web-common/src/main/groovy/org/grails/web/json/JSONWriter.java#L258
Hope This helps!
Thanks,
SA
I am using PHP league's Fractal as the transformer for my API. However, I think I must be doing something wrong as the item transformer wraps everything in an array like it would a collection which is against the JSON API standard I believe.
So for a user with ID of one I get something like this:
{
"users":[
{
"id":1,
"firstName":"Jacob",
"surname":"Windsor",
}
]
}
When surely it should be this?
{
"users":
{
"id":1,
"firstName":"Jacob",
"surname":"Windsor",
}
}
I am using ember.js and this is causing problems with naming conventions.
I am using Laravel and in my userController I have something like this:
public function show($id)
{
$user = User::find($id);
return $this->respondItem($user);
}
Then in the apiController that everything extends from:
public function respond($response, $status = 200){
return Response::make($response, $status);
}
public function respondTransform($resource){
$fractal = new Fractal\Manager();
$fractal->setSerializer(new JsonApiSerializer());
return $this->respond($fractal->createData($resource)->toJson());
}
public function respondItem($data, $transformer = null, $namespace = null){
! isset($transformer) ? $transformer = $this->transformer : $transformer = $transformer;
! isset($namespace) ? $namespace = $this->namespace : $namespace = $namespace;
$resource = new Item($data, $transformer, $namespace);
return $this->respondTransform($resource);
}
I must be doing something wrong. The fractal docs have no examples specifically for items only collections so I am unsure what I have done.
So it seems that Fractal doesn't quite obey ember-data's conventions which is an annoying problem but very easily overcome using custom serialziers.
I have a psr-4 autoloaded file named CustomJsonSerializer which I have included in my ApiController class. If you follow the article on php league's site (posted above) its fairly easy to do. I have these two methods.
public function collection($resourceKey, array $data)
{
return array($resourceKey ?: 'data' => $data);
}
/**
* Serialize an item resource
*
* #param string $resourceKey
* #param array $data
*
* #return array
*/
public function item($resourceKey, array $data)
{
return [$resourceKey => $data];
}
You can see that the collection is responding as it normally would, i.e I haven't changed it. But the item method just responds without the extra array. Simple! You have to include all the other methods as well and I haven't got round to sorting out pagination but it should be fairly simple.
I hope this helps anyone wanting to use ember-data with Fractal. I highly recommend it, fractal has made my life so much easier. You could build transformers yourself but it makes it so much easier and more easily modified in the future.
Edit:
Please make sure you keep the $resourceKey in both the methods. You need to be using it and setting it when calling the transformer. |Ember-data requires a resource key.
Assuming your userController extends ApiController, you could simply do:
public function show($id)
{
$user = User::findOrFail($id);
return $this->setStatusCode(200)->withItem($user, new UserTransformer);
}
You do need to implement the UserTransformer class. If you need help with that, let me know in the comments.
I actually found that a much simpler adjustment of JsonApiSerializer did what I needed for Ember:
(I just took out the count($data) check)
<?php
namespace Acme\Serializer;
use RuntimeException;
use League\Fractal\Serializer\JsonApiSerializer;
class EmberSerializer extends JsonApiSerializer
{
/**
* Serialize the top level data.
*
* #param string $resourceKey
* #param array $data
*
* #return array
*/
public function serializeData($resourceKey, array $data)
{
if (! $resourceKey) {
throw new RuntimeException('The $resourceKey parameter must be provided when using '.__CLASS__);
}
return array($resourceKey => $data);
}
}
I have successfully set up a quick test of creating a "REST-like" service that returns an object serialized to JSON, and that was quite easy and quick (based on this article).
But while returning JSON-ified objects was easy as peach, I have yet to see any examples dealing with input parameters that are not primitives. How can I pass in a complex object as an argument? I am using Apache CXF, but examples using other frameworks like Jackson are welcome too :)
Client side would probably be something like building a javascript object, pass it into JSON.stringify(complexObj), and pass that string as one of the parameters.
The service would probably look something like this
#Service("myService")
class RestService {
#GET
#Produces("application/json")
#Path("/fooBar")
public Result fooBar(#QueryParam("foo") double foo, #QueryParam("bar") double bar,
#QueryParam("object") MyComplex object) throws WebServiceException {
...
}
}
Sending serialized objects as parameters would probably quickly touch the 2KB URL-limit imposed by Internet Explorer. Would you recommend using POST in these cases, and would I need to change much in the function definitions?
After digging a bit I quickly found out there are basically two options:
Option 1
You pass a "wrapper object" containing all the other parameters to the service. You might need to annotate this wrapper class with JAXB annotations like #XmlRootElement in order for this to work with the Jettison based provider, but if you use Jackson in stead there is no need. Just set the content type to the right type and the right message body reader will be invoked.
This will only work for POST type services of course (AFAIK).
Example
This is just an example of turning the service mentioned in the original question into one using a wrapper object.
#Service("myService")
class RestService {
#POST
#Produces("application/json")
#Path("/fooBar")
public Result fooBar(
/**
* Using "" will inject all form params directly into a ParamsWrapper
* #see http://cxf.apache.org/docs/jax-rs-basics.html
*/
#FormParam("") FooBarParamsWrapper wrapper
) throws WebServiceException {
doSomething(wrapper.foo);
}
}
class ParamsWrapper {
double foo, bar;
MyComplexObject object;
}
Option 2
You can provide some special string format that you pack your objects into and then implement either a constructor taking a string, a static valueOf(String s) or a static fromString(String s) in the class that will take this string and create an object from it. Or quite similar, create a ParameterHandler that does exactly the same.
AFAIK, only the second version will allow you to call your services from a browser using JSONP (since JSONP is a trick restricted to GET). I chose this route to be able to pass arrays of complex objects in the URI.
As an example of how this works, take the following domain class and service
Example
#GET
#Path("myService")
public void myService(#QueryParam("a") MyClass [] myVals) {
//do something
}
class MyClass {
public int foo;
public int bar;
/** Deserializes an Object of class MyClass from its JSON representation */
public static MyClass fromString(String jsonRepresentation) {
ObjectMapper mapper = new ObjectMapper(); //Jackson's JSON marshaller
MyClass o= null;
try {
o = mapper.readValue(jsonRepresentation, MyClass.class );
} catch (IOException e) {
throw new WebApplicationException()
}
return o;
}
}
A URI http://my-server.com/myService?a={"foo":1, "bar":2}&a={"foo":100, "bar":200} would in this case be deserialized into an array composed of two MyClass objects.
2019 comment:
Seeing that this answer still gets some hits in 2019, I feel I should comment. In hindsight, I would not recomment option 2, as going through these steps just to be able to be able to do GET calls adds complexity that's probably not worth it. If your service takes such complex input, you will probably not be able to utilize client side caching anyway, due to the number of permutations of your input. I'd just go for configuring proper Cross-Origin-Sharing (CORS) headers on the server and POST the input. Then focus on caching whatever you can on the server.
The accepted answer is missing #BeanParam. See
https://docs.jboss.org/resteasy/docs/3.0-rc-1/javadocs/javax/ws/rs/BeanParam.html
for further details. It allows you to define query params inside a wrapper object.
E.g.
public class TestPOJO {
#QueryParam("someQueryParam")
private boolean someQueryParam;
public boolean isSomeQueryParam() {
return someQueryParam;
}
public boolean setSomeQueryParam(boolean value) {
this.someQueryParam = value;
}
}
... // inside the Resource class
#GET
#Path("test")
public Response getTest(#BeanParam TestPOJO testPOJO) {
...
}
the best and simplest solution is to send your object as a json string and in server side implement a method which will decode that json and map to the specified object as per your need.. and yes it`s better to use POST.