Hi

On Wed, May 25, 2011 at 10:29 PM, Carl-Erik Kopseng <[email protected]> wrote:
> Quick sum-up of the problem:
> I need to somehow configure a JAX-RS client implemented using CXF's
> proxy-based client API, to automatically convert parameters annotated
> with @FormParam to JSON. I cannot get this to work today.
> I pasted the example code on PasteBin for better viewing:
> http://www.pastebin.ca/2069097
>
> I have created some services in Apache CXF JAX-RS that takes several
> potentially complex objects as arguments. By implementing
> valueOf(String s) for complex beans + using @FormParam, I can
> pass several JSON encoded objects to the "method", not just one,
> as would normally be the case.
> This might not be the best way, or even a good way, to achieve the goal
> of being able to have several arbitrarily complex objects as parameters in
> a POST service, but it is the one I could think of ...
> Feel free to suggest a better way ;)
>

Can you clarify please, does the service need to handle form url
encoded submissions, or
have you decided to post name=value sequences and use FormParam
because you'd like to or may be need to have multiple parameters in a
resource method signature (such as jsonTest) ?

Also, do you need to use JSON or do you use it because Jackson offers
a useful Mapper class which can convert a JSON sequence into a complex
bean.

Just would like to understand your requirements better. CXF offers an
extension such as FormParam(""), so you can have

(@FormParam("myPrimitive") int myPrimitive, @FormParam("") MyObject myComplex)

but that will work only for plain name/value pairs, but won't when
values are actually JSON sequences.
If you don't need to handle form submissions then having a single
composite complex object as a parameter is what I'd suggest.

> An example implementation could be the following:
>
> @Service("receiverService")
> @Path("/receiver")
> public class ObjectReceiverService {
>   @POST
>   @Path("jsonTest")
>   @Produces("application/json")
>   public MyObject jsonTest(@FormParam("myPrimitive") int
> myPrimitive, @FormParam("myComplex") MyObject myComplex) {
>       return myComplex;
>   }
>       ...//
> }
>
> //domain class with Mapper from Jackson
> public class MyObject {
>   public String foo;
>   public String bar;
>   public String baz;
>
>   public static MyObject valueOf(String s) throws Exception {
>       return new ObjectMapper().readValue(s, MyObject.class);
>   }
>       ...//implementation of equals() etc.
> }
>
> This works quite nicely. So far I have tested it by resorting to
> low-level stuff; manually constructing query strings using Jackson's
> ObjectMapper etc. Example:
>
> //test class with lots of manual setup using WebClient
> public class ObjectReceiverServiceTest {
>   @Before
>   public void setup() {
>       ProviderFactory.getSharedInstance().registerUserProvider(new
> JacksonJsonProvider());
>   }
>   @org.junit.Test
>   public void jsonTest1_recievingSameObject() throws Exception {
>       String url = "http://localhost:8080/";;
>       String path = "rest/receiver/jsonTest";
>
>       MyObject myObjectToSend = new MyObject();
>       MyObject result = new MyObject();
>       myObjectToSend.foo = "foo";
>       myObjectToSend.bar = "bar";
>       myObjectToSend.baz = "baz";
>
>       WebClient client = WebClient.create(url);
>       client.path(path);
>       
> client.accept("application/json").type(MediaType.APPLICATION_FORM_URLENCODED_TYPE);
>       Response r = client.post("myPrimitive=123&myComplex=" + new
> ObjectMapper().writeValueAsString(myObjectToSend));
>
>       result = new ObjectMapper().readValue(new BufferedReader(new
> InputStreamReader((InputStream) r.getEntity())).readLine(),
> MyObject.class);
>       assertEquals(myObjectToSend, result);
>   }
>
> What I would like to do, is to use the proxy based api, as
> demonstrated in the CXF docs. This would be far cleaner and faster to
> implement. The previous test would be something like
>
> @org.junit.Test
> public void jsonTest2_recievingSameObject() throws Exception {
>   String url = "http://localhost:8080/rest";;
>   ObjectReceiverService service = JAXRSClientFactory.create(url,
> ObjectReceiverService.class);
>
>   MyObject myObjectToSend = new MyObject();
>   myObjectToSend.foo = "foo2";
>   myObjectToSend.bar = "bar2";
>   myObjectToSend.baz = "baz2";
>   MyObject result = service.jsonTest(123,myObjectToSend);
>
>   assertEquals(myObjectToSend, result);
> }
>
> Today, this throws an WebApplicationException in
> org.apache.cxf.jaxrs.provider.FormEncodingProvider.writeTo(..), as it
> is probably just expecting primitives.
>
> I am not quite sure how to deal with this. Any ideas? I tried
> overriding toString() with { return new
> ObjectMapper.writeValueAsString(this); }, which is ok, but
> FormEncodingProvider.writeTo still fails when trying to convert
> non-string objects,
> like an Integer to String.

Yes, it does expect Strings only - I think the fix needs to be applied
at the client side, before FormEncodingProvider is invoked, because
JAX-RS requires the support for MultivaluedMap<String, String> - I'll
look into it

Cheers, Sergey

>
> Regards
> Carl-Erik
>

Reply via email to