I am developing some REST services, in J2EE environment, using RESTeasy library.
If I have this #POST REST method:
#POST
#Path("/filter")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public List<Order> findOrders(
#FormParam("orderfiltercriteria") OrderFilterCriteria orderFilterCriteria,
#FormParam("readcontext") OrderReadContext readContext,
#FormParam("querycontrol") QueryControl queryControl,
#FormParam("fetchpattern") FetchPattern fetchPattern,
#FormParam("loadpattern") LoadPattern loadPattern)
{
...
}
Supposing that the client sends these data through a form, and supposing that these are JSON string, how can I convert (server-side) these JSON strings in the correct way? For example, the JSON string for "readcontext" field, how can be converted properly into the OrderReadContext object?
I am using WidlFly 8.2 for the deploy.
Thanks in advance.
From the #FormParam documentation:
The type T of the annotated parameter must either:
Be a primitive type
Have a constructor that accepts a single String argument
Have a static method named valueOf or fromString that accepts a single >String argument (see, for example, Integer.valueOf(String))
Be List, Set or SortedSet, where T satisfies 2 or 3 above. The resulting collection is read-only.
Related
What are the significant diffs between:
org.json.JSONObject and javax.json.JsonObject?
Most importantly are they interchangeable from client to Webservice?
ie. can I send JSONObject to Webservice and have the Webservice believe the type is JsonObject (and vice versa)?
(JSONObject found in the json-20080701.jar of ACRA)
(JsonObject found in C:\glassfish4\glassfish\modules\javax.json.jar)
What are the significant diffs between org.json.JSONObject and
javax.json.JsonObject?
javax.json.JsonObject is included in Java EE 7
javax.json.JsonObject is immutable
org.json.JSONObject has siginificantly more convenience methods
Most importantly are they interchangeable from client to Webservice?
ie. can I send JSONObject to Webservice and have the Webservice
believe the type is JsonObject (and vice versa)?
Of course this should work. It is not the class instance which gets transferred to the webservice, but the JSON data, which is generated from the class instance. On the other side, the JSON data can be parsed back into any kind of object.
Example:
If you have a simple class named Person:
public class Person {
private String name = "Hans";
private int age = 26;
}
This could be transformed into JSON similar to: {"name":"Hans", "age":25}
The generated JSON string is sent to the webservice.
Now, on the other end of your application, or in any other application, this JSON string can be parsed into any class, if you have an appropriate parser. You don't even need Java to parse it.
I have this Data Object with an Int64 column:
[TableAttribute(Name="dbo.vw_RelationLineOfBusiness")]
[DataServiceKey("ProviderRelationLobId")]
public partial class RelationLineOfBusiness
{
#region Column Mappings
private System.Guid _Lineofbusiness;
private System.String _ContractNumber;
private System.Nullable<System.Int32> _ProviderType;
private System.String _InsuredProviderType;
private System.Guid _ProviderRelationLobId;
private System.String _LineOfBusinessDesc;
private System.String _CultureCode;
private System.String _ContractDesc;
private System.Nullable<System.Guid> _ProviderRelationKey;
private System.String _ProviderRelationNbr;
**private System.Int64 _AssignedNbr;**
When I post/Put object through my OData controller using HttpClient and NewtsonSoft:
partial class RelationLineOfBusinessController : ODataController
{
public HttpResponseMessage PutRelationLineOfBusiness([FromODataUri] System.Guid key, Invidasys.VidaPro.Model.RelationLineOfBusiness entity)
the entity object is null and the error in my modelstate :
"Cannot convert a primitive value to the expected type 'Edm.Int64'. See the inner exception for more details."
I noticed when I do a get on my object using the below URL:
Invidasys.Rest.Service/VidaPro/RelationLineOfBusiness(guid'c6824edc-23b4-4f76-a777-108d482c0fee')
my json looks like the following - I noticed that the AssignedNbr is treated as a string.
{
"odata.metadata":"Invidasys.Rest.Service/VIDAPro/$metadata#RelationLineOfBusiness/#Element",
"Lineofbusiness":"ba129c95-c5bb-4e40-993e-c28ca86fffe4","ContractNumber":null,"ProviderType":null,
"InsuredProviderType":"PCP","ProviderRelationLobId":"c6824edc-23b4-4f76-a777-108d482c0fee",
"LineOfBusinessDesc":"MEDICAID","CultureCode":"en-US","ContractDesc":null,
"ProviderRelationKey":"a2d3b61f-3d76-46f4-9887-f2b0c8966914","ProviderRelationNbr":"4565454645",
"AssignedNbr":"1000000045","Ispar":true,"ProviderTypeDesc":null,"InsuredProviderTypeDesc":"Primary Care Physician",
"StartDate":"2012-01-01T00:00:00","EndDate":"2014-01-01T00:00:00","Created":"2014-06-13T10:59:33.567",
"CreatedBy":"Michael","Updated":"2014-06-13T10:59:33.567","UpdatedBy":"Michael"
}
When I do a PUT with httpclient the JSON is showing up in my restful services as the following and the json for the AssignedNbr column is not in quotes which results in the restful services failing to build the JSON back to an object. I played with the JSON and put the AssignedNbr in quotes and the request goes through correctly.
{"AssignedNbr":1000000045,"ContractDesc":null,"ContractNumber":null,"Created":"/Date(1402682373567-0700)/",
"CreatedBy":"Michael","CultureCode":"en-US","EndDate":"/Date(1388559600000-0700)/","InsuredProviderType":"PCP",
"InsuredProviderTypeDesc":"Primary Care Physician","Ispar":true,"LineOfBusinessDesc":"MEDICAID",
"Lineofbusiness":"ba129c95-c5bb-4e40-993e-c28ca86fffe4","ProviderRelationKey":"a2d3b61f-3d76-46f4-9887-f2b0c8966914",
"ProviderRelationLobId":"c6824edc-23b4-4f76-a777-108d482c0fee","ProviderRelationNbr":"4565454645","ProviderType":null,
"ProviderTypeDesc":null,"StartDate":"/Date(1325401200000-0700)/","Updated":"/Date(1408374995760-0700)/","UpdatedBy":"ED"}
The reason we wanted to expose our business model as restful services was to hide any data validation and expose all our databases in format that is easy to develop against. I looked at the DataServiceContext to see if it would work and it does but it uses XML to communicate between the restful services and the client. Which would work but DataServiceContext does not give the level of messaging that HttpRequestMessage/HttpResponseMessage gives me for informing users on the errors/missing information with their post.
We are planning on supporting multiple devices from our restful services platform but that requires that I can use NewtonSoft Json as well as Microsoft's DataContractJsonSerializer if need be.
My question is for a restful service standpoint - is there a way I can configure/code the restful services to take in the AssignedNbr as in JSON as without the quotes.
Or from a JSON standpoint is their a way I can get the JSON built without getting into the serializing business nor do I want our clients to have deal with custom serializers if they want to write their own apps against our restful services.
Any suggestions?
Thanks.
I think you can migrate to Web API 2.2 for OData V4. Here's the information:
Announcing the Release of ASP.NET MVC 5.2, Web API 2.2 and Web Pages 3.2
OData V4 Spec says:
3.2 Controlling the Representation of Numbers
The IEEE754Compatible=true format parameter indicates that the service MUST serialize Edm.Int64 and Edm.Decimal numbers (including the odata.count, if requested) as strings. If not specified, or specified as IEEE754Compatible=false, all numbers MUST be serialized as JSON numbers.
This enables support for JavaScript numbers that are defined to be 64-bit binary format IEEE 754 values [ECMAScript] (see section 4.3.1.9) resulting in integers losing precision past 15 digits, and decimals losing precision due to the conversion from base 10 to base 2.
OData JSON payloads that format Edm.Int64 and Edm.Decimal values as strings MUST specify this format parameter in the media type returned in the Content-Type header.
So, for payload as:
#"{
""Lineofbusiness"": ""ba129c95-c5bb-4e40-993e-c28ca86fffe4"",
""AssignedNbr"": ""1000000045""
}";
you should set:
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json;IEEE754Compatible=true");
Otherwise, you shouldn't.
Sam Xu is exactly right and should be marked as the answer.
However, I wanted to add exactly what you need to do to add this to the pipeline.
First, you can set this global, per route etc. You can find that information here:
http://www.asp.net/web-api/overview/advanced/http-message-handlers
Below you'll find an example that will work.
public static void Configuration(IAppBuilder builder)
{
HttpConfiguration config = new HttpConfiguration();
config.MessageHandlers.Add(new MethodOverrideHandler());
}
public class MethodOverrideHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json;IEEE754Compatible=true");
return base.SendAsync(request, cancellationToken);
}
}
Alternatively, try changing the type you send to your web api to Number instead of string
Also, check the Type of the decimal that you are sending. If it's type 'string' you can change it to type number. For my service, making this change no longer throws the error.
//Gnuget Package Manager Install-Package numeral
if (typeof newValue === 'string')
{
newValue = numeral().unformat(newValue);
}
odatajs.oData.request(
{
requestUri: xxx,
method: "PATCH",
data: { PriceNull: newValue }
}
I am using Jersey rest service. My resource object takes a Object as shown below as post parameter. From the client side
I am setting key values pair in properties field of my input object. One of the k,v pairs were (key,value)=("timestamp",new java.util.Date()). Once I have InputObject ready, I use json to serialize the data and send to RestService
when the request is received by my rest resource, I am getting the value corresponding to the key timestamp as a long.
I wanted to know if it is possible some how to get back value as java.util.Date e.g some kind of configuration or overridding capability in jersey
I am interested in standard way of handling this problem, there are few hacks that I know, I would apply them if only std way is not possible to achieve my requirement
public class InputObject {
private long id,
private Map<String,Object> map;
// not adding the getters and setters
}
Following is what my resource object accepts.
#POST
#Path("/forceevent")
#Consumes("application/json")
#Produces("application/json")
public AckValue sendEvent(InputObject request)
{
return AckValue.FAILURE;
}
I'm having issues using Jackson to map a Javascript posted JSON array of hashes (Tag).
Here is the data received by the controller #RequestBody (It is send with correct json requestheader):
[{name=tag1}, {name=tag2}, {name=tag3}]
Here is the controller:
#RequestMapping(value = "purchases/{purchaseId}/tags", method = RequestMethod.POST, params = "manyTags")
#ResponseStatus(HttpStatus.CREATED)
public void createAll(#PathVariable("purchaseId") final Long purchaseId, #RequestBody final List<Tag> entities)
{
Purchase purchase = purchaseService.getById(purchaseId);
Set<Tag> tags = purchase.getTags();
purchaseService.updatePurchase(purchase);
}
When I debug and view the 'entities' value it shows as an ArrayList of generic objects, not as a list of objects of type 'Tag' as I would expect.
How can I get jackson to map a passed array of objects to a list of obejcts of type 'Tag'?
Thanks
It sounds like Spring is not passing full type information for some reason, but rather a type-erased version, as if declaration was something like List<?> tag. I don't know what can be done to fully resolve this (may need something from Spring integration team), but one work-around is to define your own type like:
static class TagList extends ArrayList<Tag> { }
and use that instead. This will retain generic parameterization through super-type declarations so that even if Spring only passes equivalent of TagList.class, Jackson can figure out the Tag parameter.
Another way to do this is to rather obtain an array than a List, as follows:
#RequestBody Tag[] entities
Jackson requires a default constructor with no parameters on custom Objects, so you'll need to simply add a default constructor to your Tag class.
In your case simply add to your Tag class:
public Tag(){}
For some reason, I haven't found any normal way to do the following:
I want to Post a json object, and add additional parameters to the call (in this case, an authentication token).
This is a simple RESTful server in myUrl/server, which should give access to different resources of a "person" in the url myUrl/server/person/personCode/resourceName.
GET is easy, and requires no object, only parameters.
The problem arrises when I get to POST - how do I attach the JSON, and keep the other parameters as well?
The class (much has been removed for clarity and proprietary reasons...):
//Handles the person's resources
#Path("/person/{personCode}/{resourceName}")
public class PersonResourceProvider {
#GET
#Produces("application/json")
public String getPersonResource(#PathParam("personCode") String personCode, #PathParam("resourceName") String resourceName, #DefaultValue("") #QueryParam("auth_token") String auth_token) throws UnhandledResourceException, UnauthorizedAccessException {
//Authenticates the user in some way, throwing an exception when needed...
authenticate(personCode, auth_token, resourceName);
//Returns the resource somehow...
}
#POST
#Produces("application/json")
public String postPersonResource(#PathParam("personCode") String personCode, #PathParam("resourceName") String resourceName, #DefaultValue("") #QueryParam("resourceData") String resourceData, #DefaultValue("") #QueryParam("auth_token") String auth_token) throws UnhandledResourceException, UnauthorizedAccessException {
//Again, authenticating
authenticate(personCode, auth_token, resourceName);
//Post the given resource
}
}
Now, the GET method works perfectly, when you go to
myUrl/person/personCode/resourceName, it gives me the correct resource.
The auth_token is used with every single call to the server (for now, authentication is done by comparing with a predefined string), so it's needed. All the other parameters are provided through the path, except for the authentication token, which should not be in the path as it does not relate to the identity of the required resource.
When I get to POST, it's a problem.
I know there's a way to tell the method it consumes a JSON, but in that case, what will happen to the other parameters (auth_token is one of them)?
Should I use Multipart?
Another related question, this is the first time I've designed such a server, is this design correct?
Thanks!
I am not sure I understand what you are trying to achieve. Let me try explain a few things - hope it will be relevant to your question:
#QueryParam injects parameters which are part of your path - i.e. the part of the URL that goes after "?".
E.g. if you have a URL like this:
http://yourserver.com/person/personCode/resourceName?resourceData=abc&token=1234
Then there would be 2 query params - one named resourceData with value "abc" and the other one named token with value "1234".
If you are passing an entity in the POST request, and that entity is of application/json type, you can simply annotate your post method using #Consumes("application/json") annotation and add another parameter to your method, which does not need to be annotated at all.
That parameter can be either a String (in that case Jersey would pass a raw JSON string and you would have to parse it yourself) or it can be a java bean annotated with #XmlRootElement annotation - in that case (if you also include jersey-json module on your classpath) Jersey will try to unmarshall the json string into that object using JAXB. You can also use Jackson or Jettison libraries to do that - see this section of Jersey User Guide for more info: http://jersey.java.net/nonav/documentation/latest/json.html
Found!
Client side:
Client c = Client.create();
WebResource service = c.resource("www.yourserver.com/");
String s = service.path("test/personCode/resourceName")
.queryParam("auth_token", "auth")
.type("text/plain")
.post(String.class, jsonString);
Server side:
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
#Path("/test/{personCode}/{resourceName}")
public class TestResourceProvider {
#POST
#Consumes("text/plain")
#Produces("application/json")
public String postUserResource(String jsonString,
#PathParam("personCode") String personCode,
#PathParam("resourceName") String resourceName,
#QueryParam("auth_token") String auth_token)
throws UnhandledResourceException {
//Do whatever...
}
}
In my case, I will parse the json I get in the server depending on the resource name, but you can also pass the object itself, and make the server consume an "application/json".