I need to map a JSON object but the problem is that it has an inner custom list. How could use RestTemplate in this case?
I am trying to use ResponseEntity and ParameterizedTypeReference but I have not found the solution yet.
{
"results":{
"ALL":{
"currencyName":"Albanian Lek",
"currencySymbol":"Lek",
"id":"ALL"
},
"XCD":{
"currencyName":"East Caribbean Dollar",
"currencySymbol":"$",
"id":"XCD"
},
"EUR":{
"currencyName":"Euro",
"currencySymbol":"€",
"id":"EUR"
},
"BBD":{
"currencyName":"Barbadian Dollar",
"currencySymbol":"$",
"id":"BBD"
},
"BTN":{
"currencyName":"Bhutanese Ngultrum",
"id":"BTN"
},
"BND":{
"currencyName":"Brunei Dollar",
"currencySymbol":"$",
"id":"BND"
},
"XAF":{
"currencyName":"Central African CFA Franc",
"id":"XAF"
},
"CUP":{
"currencyName":"Cuban Peso",
"currencySymbol":"$",
"id":"CUP"
},
"USD":{
"currencyName":"United States Dollar",
"currencySymbol":"$",
"id":"USD"
}
}
}
// you can create a custom class like below and try to map it
class NodeWrapper{
private Map<String, NodeData> results;
}
class NodeData{
private String currencyName;
private String currencySymbol;
private id;
}
// also allow nulls using object mapper annotations
Related
I am trying to deserialize JSON from consuming an API.
However the JSON body coming from the API is not consistent. Sometimes it comes as a list, and sometimes as a single item.
Example:
"Charge": [
{
"ChargeType": "EXPRESS 12:00",
"ChargeAmount": 0.0
},
{
"ChargeCode": "YK",
"ChargeType": "12:00 PREMIUM",
"ChargeAmount": 0.0
},
]
And in another case:
"Charge": {
"ChargeType": "EXPRESS",
"ChargeAmount": 8.5
}
I am using RestTemplate and DTOs.
My DTO is built like this.
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Charges {
#JsonProperty(value = "Currency")
private String currency;
#JsonProperty(value = "Charge")
private List<Charge> charges;
}
This fails on the case when it comes as an object:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList<Charge>` out of START_OBJECT token
Is there a way I can solve this without creating my Custom JSON Converter?
And if I have to create it, how can I do it?
Solved by using:
#JsonProperty(value = "Charge")
#JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
private List<Charge> charges;
I am fairly new to JAX-RS so bear with me on this question. I'm trying to consume a REST API using JAX-RS framework. In particular I am trying to invoke a HTTP GET method on a resource where the response entity will be in JSON format. Up until now I can parse the returned JSON into a customized class with the following code snippet;
WebTarget target = client.target(url);
Builder builder = target.request(MediaType.APPLICATION_JSON);
myClass obj = builder.get(myClass.class);
However, in my latest GET request the JSON return will be best abstracted as a collection of objects. I know in .NET this can be done fairly easily with
JsonConvert.DeserializeObject<List<myClass>>
but how could I do that in JAX-RS? Thanks in advance.
EDIT:
I model my code after the solution in How to get list<String> as response from jersey2 client
WebTarget target = client.target(url);
Builder builder = target.request(MediaType.APPLICATION_JSON);
builder.header(X_OCTOPUS_APIKEY_NAME, apiKey);
Response serviceResponse = builder.get(Response.class);
List<myType> objects = serviceResponse.readEntity(new GenericType<List<myType>>() {});
However the objects returned is always null. To verify the REST API call actually return a valid JSON value I replace the last line with:
String strDate = serviceResponse.readEntity(String.class);
It is confirmed with the following JSON return:
[
{
"Id": "Users-267",
"Username": "mdamon#mydomain.com",
"DisplayName": "Damon, Matt",
"IsActive": true,
"IsService": false,
"EmailAddress": "mdamon#mydomain.com",
"IsRequestor": false,
"Links": {
"Self": "/api/users/Users-267",
"Permissions": "/api/users/Users-267/permissions",
"ApiKeys": "/api/users/Users-267/apikeys{/id}{?skip}",
"Avatar": "https://www.gravatar.com/avatar/94324e7c54a9a5f9d103b2a709863fc3?d=blank"
}
},
{
"Id": "Users-2101",
"Username": "baffleck#mydomain.com",
"DisplayName": "Affleck, Ben",
"IsActive": true,
"IsService": false,
"EmailAddress": "baffleck#mydomain.com",
"IsRequestor": false,
"Links": {
"Self": "/api/users/Users-2101",
"Permissions": "/api/users/Users-2101/permissions",
"ApiKeys": "/api/users/Users-2101/apikeys{/id}{?skip}",
"Avatar": "https://www.gravatar.com/avatar/11edd32712facde9a7d3dd4445a4abe9?d=blank"
}
},
...
]
So for reason the JSON is not being parsed at a collection of my custom type. One extra piece of information is my custom is defined as follows:
#XmlRootElement
public class myType {
String DisplayName;
String EmailAddress;
public myType() {
super();
}
public void setDisplayName(String displayName) {
DisplayName = displayName;
}
public String getDisplayName() {
return DisplayName;
}
public void setEmailAddress(String emailAddress) {
EmailAddress = emailAddress;
}
public String getEmailAddress() {
return EmailAddress;
}
}
I only include the DisplayName and EmailAddress field of the JSON in my custom type because I don't need all the other data, in case that matters. Can anyone tell me why it is not being parsed? Thanks
how I can parse following code retrieved in a textbox:
{
"items": [
{
"snippet": {
"channelId": "UCcTbyoZjhqoCn4yVawpMFDA",
"title": "Forever - Stratovarius",
"categoryId": "10"
},
"statistics": {
"viewCount": "6180411",
"likeCount": "19060",
"dislikeCount": "342",
"favoriteCount": "0",
"commentCount": "3025"
}
}
]
}
My code for get value title and likeCount:
Dim url As String = "https://www.googleapis.com/youtube/v3/videos?id=8BvV9arABLs&key=KEYAPI&fields=items(id,snippet(channelId,title,categoryId),statistics)&part=snippet,statistics"
Dim json As String = New WebClient().DownloadString(url)
Dim root As JToken = JToken.Parse(json)
Dim sb As New StringBuilder()
For Each item As JToken In root("items")
textbox1.text=sb.AppendLine(item.SelectToken("title") & sb.AppendLine(item.SelectToken("likeCount"))
Next
First, it's better to format marked up data like JSON before you post it.
Like this:
{
"items" : [{
"snippet" : {
"channelId" : "UCcTbyoZjhqoCn4yVawpMFDA",
"title" : "Forever - Stratovarius",
"categoryId" : "10"
},
"statistics" : {
"viewCount" : "6180411",
"likeCount" : "19060",
"dislikeCount" : "342",
"favoriteCount" : "0",
"commentCount" : "3025"
}
}
]
}
As #rufanov said there are a lof of packages for JSON serialization.
As for me, I use Newtonsoft JSON. I use it in C# and will write examples in it, but I strongly believe it should be the same or similar on VB.NET.
Create classes for JSON objects. Pay attention: the members should be names as in JSON object.
public class JItemArray
{
public JItem[] items;
}
public class JItem
{
public JSnippet snippet;
public JStatistics statistics;
}
public class JSnippet
{
public string channelId;
public string title;
public string categoryId;
}
public class JStatistics
{
public string viewCount;
public string likeCount;
public string dislikeCount;
public string favoriteCount;
public string commentCount;
}
Then, you will be able to do
JItemArray itemArray = JsonConvert.DeserializeObject<JItemArray>(yourJsonString);
It will throw JsonReaderException (with explanation in Message) if something fails.
Else, it will return a proper JItem .NET object with all necessary parsed data.
Here it is on NuGet website.
https://www.nuget.org/packages/Newtonsoft.Json/
But if you only need these two values it's pretty good practice to use JToken.
There is many NuGet packages for working with JSON. Use one of them.
I am desperately looking for a way to make JSON serialization with root element to work on JBoss AS 7.1 with RestEasy and Jettison provider enabled.
Although, according to RestEasy documentation, returning of the JSON root element should work, I never retrieve when requesting a REST servlet.
I use an Object Factory:
#XmlRegistry
public class ObjectFactory {
private final static QName _NotificationList_QNAME = new QName("xxx:xxx:xxx", "notificationList");
public NotificationList createNotificationList() {
return new NotificationList();
}
#XmlElementDecl(namespace = "xxx:xxx:xxx", name = "notificationList")
public JAXBElement<NotificationList> createNotificationList(NotificationList value) {
return new JAXBElement<NotificationList>(_NotificationList_QNAME, NotificationList.class, null, value);
}
}
With the following XML object:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "NotificationList", namespace = "xxx:xxx:xxx:xxx", propOrder = {
"any"
})
#XmlRootElement (name = "notificationList" )
public class NotificationList {
#XmlAnyElement(lax = true)
protected List<Object> any;
#XmlElement (name="notificationList")
public List<Object> getAny() {
if (any == null) {
any = new ArrayList<Object>();
}
return this.any;
}
}
I expect a notification list to be returned with root element "notificationList" but I don't get it. I use Jettison provider by default but also switched to Jackson. Both do not work for me.
Maybe it is worth mentioning, the REST method is not returning the object itself but passes a AsynchronousResponse to another object which processes and eventually returen the JSON object bacck to I use AsynchronousResponse when creating the response
Edit:
Some more info on the classes actually using NotificationList:
The following REST class consumes a NotificationChannel class (not of interest here) and passes an Asynchronous Response obejct to another classe. This response object eventually returns the NotificationList. In a simplified way, as follows:
#Path("/notificationchannel/v1")
public class NotificationChannelService {
#POST
#Path("/{userid}/channels")
#Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#Mapped(namespaceMap = {
#XmlNsMap(namespace = "XXXX:XXXX:XXXX:XXXX", jsonName = "notificationChannel")
})
public void createNotificationChannel(
final #Suspend(10000) AsynchronousResponse response,
final JAXBElement<NotificationChannel> ncParam,
#PathParam("userid") String userID) {
NotificationManager nMan = new NotificationManager(resp);
}
}
The response is created and returned as follows:
public class NotificationManager {
public NotificationManater(AsynchronousResponse resp){
//dostuff
notificationList = objectFatory.creatNotificationList();
//add notification object (also defined int ObjectFactory)
notificaitonList.addObject(messageNotification)
notificaitonList.addObject(statusNotification)
notificaitonList.addObject(inviteNotification)
//dostuff
resp.setResponse(Response.created(UriBuilder.fromUri(nc.getResourceURL()).build())
.entity(notificationList)
.type(MediaType.APPLICATION_JSON)
.build());
}
}
On client side, i expect the following response:
{"notificationList": {
"inboundMessageNotification": {"inboundMessage": {
"destinationAddress": "tel:+19585550100",
"inboundMMSMessage": {"subject": "Who is RESTing on the beach?"},
"link": {
"href": "http://example.com/exampleAPI/v1/messaging/inbound/subscriptions/sub123",
"rel": "Subscription"
},
"messageId": "msg123",
"resourceURL": "http://example.com/exampleAPI/v1/messaging/inbound/registrations/reg123/messages/msg123 ",
"senderAddress": "tel:+19585550101"
}},
"presenceNotification": {
"callbackData": "1234",
"link": {
"href": "http://example.com/exampleAPI/v1/presence/tel%3A%2B19585550101/subscriptions/presenceSubscriptions/ tel%3A%2B19585550100/sub001",
"rel": "PresenceSubscription"
},
"presence": {"person": {"mood": {"moodValue": "Happy"}}},
"presentityUserId": "tel:+19585550100",
"resourceStatus": "Active"
}
}}
But i do get this (no RootElement name, no notification object name):
{
{
"destinationAddress": "tel:+19585550100",
"inboundMMSMessage": {"subject": "Who is RESTing on the beach?"},
"link": {
"href": "http://example.com/exampleAPI/v1/messaging/inbound/subscriptions/sub123",
"rel": "Subscription"
},
"messageId": "msg123",
"resourceURL": "http://example.com/exampleAPI/v1/messaging/inbound/registrations/reg123/messages/msg123 ",
"senderAddress": "tel:+19585550101"
},
{
"callbackData": "1234",
"link": {
"href": "http://example.com/exampleAPI/v1/presence/tel%3A%2B19585550101/subscriptions/presenceSubscriptions/ tel%3A%2B19585550100/sub001",
"rel": "PresenceSubscription"
},
"presence": {"person": {"mood": {"moodValue": "Happy"}}},
"presentityUserId": "tel:+19585550100",
"resourceStatus": "Active"
}
}
I faced the exact same issue and the problem is that the json provider based on Jackson (resteasy-jackson-provider) is actually taking over the serialization (due to the implicit module dependency). What I did was to use a specific deployment descriptor META-INF/jboss-deployment-structure.xml with the following content.
<jboss-deployment-structure>
<deployment>
<exclusions>
<module name="org.jboss.resteasy.resteasy-jackson-provider" />
</exclusions>
<dependencies>
<module name="org.jboss.resteasy.resteasy-jettison-provider" />
</dependencies>
</deployment>
</jboss-deployment-structure>
It will force the container to switch to the jettison provider for your application.
I'm using Jersey to create a REST web service for a server component.
The JAXB-annotated object I want to serialize in a list looks like this:
#XmlRootElement(name = "distribution")
#XmlType(name = "tDistribution", propOrder = {
"id", "name"
})
public class XMLDistribution {
private String id;
private String name;
// no-args constructor, getters, setters, etc
}
I have a REST resource to retrieve one distribution which looks like this:
#Path("/distribution/{id: [1-9][0-9]*}")
public class RESTDistribution {
#GET
#Produces("application/json")
public XMLDistribution retrieve(#PathParam("id") String id) {
return retrieveDistribution(Long.parseLong(id));
}
// business logic (retrieveDistribution(long))
}
I also have a REST resource to retrieve a list of all distributions, which looks like this:
#Path("/distributions")
public class RESTDistributions {
#GET
#Produces("application/json")
public List<XMLDistribution> retrieveAll() {
return retrieveDistributions();
}
// business logic (retrieveDistributions())
}
I use a ContextResolver to customize JAXB serialization, which is currently configured like this:
#Provider
#Produces("application/json")
public class JAXBJSONContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext context;
public JAXBJSONContextResolver() throws Exception {
JSONConfiguration.MappedBuilder b = JSONConfiguration.mapped();
b.nonStrings("id");
b.rootUnwrapping(true);
b.arrays("distribution");
context = new JSONJAXBContext(b.build(), XMLDistribution.class);
}
#Override
public JAXBContext getContext(Class<?> objectType) {
return context;
}
}
Both REST resources work, as well as the context resolver. This is an example of output for the first one:
// path: /distribution/1
{
"id": 1,
"name": "Example Distribution"
}
Which is exactly what I want. This is an example of output for the list:
// path: /distributions
{
"distribution": [{
"id": 1,
"name": "Sample Distribution 1"
}, {
"id": 2,
"name": "Sample Distribution 2"
}]
}
Which is not quite what I want.
I don't understand why there is an enclosing distribution tag there. I wanted to remove it with .rootUnwrapping(true) in the context resolver, but apparently that only removes another enclosing tag. This is the output with .rootUnwrapping(false):
// path: /distribution/1
{
"distribution": {
"id": 1,
"name": "Example Distribution"
}
} // not ok
// path: /distributions
{
"xMLDistributions": {
"distribution": [{
"id": 1,
"name": "Sample Distribution 1"
}, {
"id": 2,
"name": "Sample Distribution 2"
}]
}
}
I also had to configure .arrays("distribution") to always get a JSON array, even with only one element.
Ideally, I'd like to have this as an output:
// path: /distribution/1
{
"id": 1,
"name": "Example Distribution"
} // currently works
// path: /distributions
[{
"id": 1,
"name": "Sample Distribution 1"
}, {
"id": 2,
"name": "Sample Distribution 2"
}]
I tried to return a List<XMLDistribution>, a XMLDistributionList (wrapper around a list), a XMLDistribution[], but I couldn't find a way to get a simple JSON array of distributions in my required format.
I also tried the other notations returned by JSONConfiguration.natural(), JSONConfiguration.mappedJettison(), etc, and couldn't get anything resembling what I need.
Does anyone know if it is possible to configure JAXB to do this?
I found a solution: replace the JAXB JSON serializer with a better behaved JSON serializer like Jackson. The easy way is to use jackson-jaxrs, which has already done it for you. The class is JacksonJsonProvider. All you have to do is edit your project's web.xml so that Jersey (or another JAX-RS implementation) scans for it. Here's what you need to add:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>your.project.packages;org.codehaus.jackson.jaxrs</param-value>
</init-param>
And that's all there is to it. Jackson will be used for JSON serialization, and it works the way you expect for lists and arrays.
The longer way is to write your own custom MessageBodyWriter registered to produce "application/json". Here's an example:
#Provider
#Produces("application/json")
public class JsonMessageBodyWriter implements MessageBodyWriter {
#Override
public long getSize(Object obj, Class type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return -1;
}
#Override
public boolean isWriteable(Class type, Type genericType,
Annotation annotations[], MediaType mediaType) {
return true;
}
#Override
public void writeTo(Object target, Class type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap httpHeaders, OutputStream outputStream)
throws IOException {
new ObjectMapper().writeValue(outputStream, target);
}
}
You'll need to make sure your web.xml includes the package, as for the ready-made solution above.
Either way: voila! You'll see properly formed JSON.
You can download Jackson from here:
http://jackson.codehaus.org/
The answer of Jonhatan is great and it has been very useful for me.
Just an upgrade:
if you use the version 2.x of Jackson (e.g. version 2.1) the class is com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider, therefore the web.xml is:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>your.project.packages;com.fasterxml.jackson.jaxrs.json</param-value>
</init-param>