I have a requirement where I need to convert JSON response from an API and send it as XML to the end client.
I am able to successfully receive JSON (output pasted below) from the API but unable to convert it to Java Objects using ObjectMapper. I don't get any errors; but when I return "GetCardInfo" object it is null.
I have tried searching through google but unable to find why it is not working. It will be a big help if someone can help me understand what is the issue with my code.
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.lang.Object;
import javax.annotation.Resource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.ws.Response;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.json.JSONObject;
import org.json.XML;
import org.slf4j.ext.XLogger;
import org.slf4j.ext.XLoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import com.bhn.webservice.ivr.CardActivationResponse;
import com.bhn.webservice.ivr.CardInfo;
import com.bhn.webservice.ivr.ErrorDetails;
import com.bhn.webservice.ivr.GetCardInfo;
import com.bhn.webservice.ivr.GetCardInfoReceiveJSONResponse;
import com.bhn.webservice.ivr.GetCardInfoRequest;
import com.bhn.webservice.ivr.GetCardInfoResponse;
import com.bhn.webservice.ivr.GetCardInfoSendJSONRequest;
import com.bhn.webservice.ivr.GetCardTransactionsReceiveJSONResponse;
import com.bhn.webservice.ivr.GetCardTransactionsRequest;
import com.bhn.webservice.ivr.GetCardTransactionsResponse;
import com.bhn.webservice.ivr.GetCardTransactionsSendJSONRequest;
import com.bhn.webservice.ivr.IVRKPNResponse;
import com.bhn.webservice.ivr.IVRResponse;
import com.bhn.webservice.ivr.IVRWrapperConstants;
import com.bhn.webservice.ivr.IVRWrapperResponse;
import com.bhn.webservice.ivr.RequestContext;
import com.bhn.webservice.ivr.VerifyCardConvertResponse;
import com.bhn.webservice.ivr.VerifyCardHolderReceiveJSONResponse;
import com.bhn.webservice.ivr.VerifyCardHolderRequest;
import com.bhn.webservice.ivr.VerifyCardHolderResponse;
import com.bhn.webservice.ivr.VerifyCardHolderSendJSONRequest;
import com.bhn.webservice.ivr.VerifyCardReceiveJSONResponse;
import com.bhn.webservice.ivr.VerifyCardRequest;
import com.bhn.webservice.ivr.VerifyCardResponse;
import com.bhn.webservice.ivr.VerifyCardSendJSONRequest;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
//XML mapper.
ObjectMapper mapper = new XmlMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
mapper.enable(SerializationFeature.INDENT_OUTPUT);
GetCardInfo gci = mapper.readValue(JSONResponse.toString(),GetCardInfo.class);
Below is the output from JSONResponse.toString()
{
"transactionId" : "RNQFBKGPZ4M18PLZJA4BDGC32W",
"isCompleted" : true,
"percentComplete" : "100",
"card" : {
"id" : "1000000000000098718",
"bin" : "451129",
"proxyCardNumber" : "603953510161946xxxx",
"isActive" : false,
"isRegistered" : false,
"expirationDate" : "2017-06-30T23:59:59.000+0000",
"serviceCode" : "121",
"balances" : {
"openingBalance" : "5000",
"closingBalance" : "5000",
"pendingBalance" : "5000",
"currencyCode" : "USD"
},
"status" : "OPEN",
"statusReason" : "NONE",
"provisionType" : "PHYSICAL",
"accountStatus" : "OPEN",
"accountStatusReason" : "NONE",
"product" : {
"id" : "1000000000000000415",
"name" : "EXM Visa Corp LAP",
"isActive" : "true",
"productIdentifier" : "07675023660",
"bin" : "451129",
"issuer" : "MetaBank"
}
}
}
Below is the class GetCardInfo
public class GetCardInfo {
#XmlElement(name = "transactionId", required = true)
public String transactionId;
#XmlElement(name = "isCompleted", required = true)
public Boolean isCompleted;
#XmlElement(name = "percentComplete", required = true)
public String percentComplete;
#XmlElement(name = "card", required = true)
public Card card; //Parent for remaining data
public static class Card {
#XmlElement(name = "id", required = true)
public String id;
#XmlElement(name = "bin", required = true)
public String bin;
#XmlElement(name = "proxyCardNumber", required = true)
public String proxyCardNumber;
#XmlElement(name = "isActive", required = true)
public Boolean isActive;
#XmlElement(name = "isRegistered", required = true)
public Boolean isRegistered;
#XmlElement(name = "expirationDate", required = true, type = String.class)
#XmlJavaTypeAdapter(Adapter1 .class)
#XmlSchemaType(name = "dateTime")
public Date expirationDate;
#XmlElement(name = "serviceCode", required = true)
public String serviceCode;
#XmlElement(name = "balances", required = true)
public Balances balances; //Parent for balances data
#XmlElement(name = "status", required = true)
public String status;
#XmlElement(name = "statusReason", required = true)
public String statusReason;
#XmlElement(name = "provisionType", required = true)
public String provisionType;
#XmlElement(name = "accountStatus", required = true)
public String accountStatus;
#XmlElement(name = "accountStatusReason", required = true)
public String accountStatusReason;
#XmlElement(name = "product", required = true)
public Product product;
#Override
public String toString() {
return "Card [id=" + id + ", bin=" + bin + ", "
+ "proxyCardNumber=" + proxyCardNumber + ", isActive=" + isActive
+ ", isRegistered=" + isRegistered + ", expirationDate=" + expirationDate
+ ", serviceCode=" + serviceCode + ", balances=" + balances
+ ", status=" + status + ", statusReason=" + statusReason
+ ", provisionType=" + provisionType + ", accountStatus=" + accountStatus
+ ", accountStatusReason=" + accountStatusReason + ", product=" + product + "]";
}
}
public static class Balances {
#XmlElement(name = "openingBalance", required = true)
public String openingBalance;
#XmlElement(name = "closingBalance", required = true)
public String closingBalance;
#XmlElement(name = "pendingBalance", required = true)
public String pendingBalance;
#XmlElement(name = "currencyCode", required = true)
public String currencyCode;
#Override
public String toString() {
return "Balance [openingBalance=" + openingBalance + ", closingBalance=" + closingBalance + ", "
+ "pendingBalance=" + pendingBalance + ", currencyCode=" + currencyCode + "]";
}
}
public static class Product {
#XmlElement(name = "id", required = true)
public String id;
#XmlElement(name = "name", required = true)
public String name;
#XmlElement(name = "isActive", required = true)
public String isActive;
#XmlElement(name = "productIdentifier", required = true)
public String productIdentifier;
#XmlElement(name = "bin", required = true)
public String bin;
#XmlElement(name = "issuer", required = true)
public String issuer;
#Override
public String toString() {
return "Card [id=" + id + ", bin=" + bin + ", "
+ "name=" + name + ", isActive=" + isActive
+ ", productIdentifier=" + productIdentifier + ", issuer=" + issuer + "]";
}
}
#Override
public String toString() {
return "GetCardInfo [transactionId=" + transactionId
+ ", isCompleted=" + isCompleted
+ ", percentComplete=" + percentComplete
+ ", card=" + card + "]";
}
}
EDIT:
I put a catch block for IOException and found that I am getting below IOException. This would mean that there is something wrong with my JSON String.
Added my POM.xml below. Also added imports above for the Java file that has the ObjectMapper.
catch (IOException e) {
logger.error(
"IOException - ",
e.getMessage());
e.printStackTrace();
}
IOException - com.ctc.wstx.exc.WstxUnexpectedCharException: Unexpected character '{' (code 123) in prolog; expected '<'
at [row,col {unknown-source}]: [1,1]
Pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<organization>
<name>xxx</name>
<url></url>
</organization>
<parent>
<groupId>com.bhn.poms</groupId>
<artifactId>component-parent-pom</artifactId>
<version>2.17</version>
<relativePath />
</parent>
<artifactId>ivr-wrapper-service</artifactId>
<groupId>com.bhn.webservice</groupId>
<version>1.2.26-SNAPSHOT</version>
<name>IVR Wrapper Service Implementation</name>
<description>This project defines the java implementation for this service.</description>
<properties>
<bhn-entity-management-version>2.32</bhn-entity-management-version>
</properties>
<dependencies>
<dependency>
<groupId>com.bhn.webservice</groupId>
<artifactId>entity-management-service</artifactId>
<version>${bhn-entity-management-version}</version>
</dependency>
<dependency>
<groupId>com.bhn.webservice</groupId>
<artifactId>ivr-wrapper-domain-model</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.bhn.webservice</groupId>
<artifactId>web-service-client</artifactId>
<version>2.41</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>fluent-hc</artifactId>
<version>4.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.6.3</version>
</dependency>
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>woodstox-core-asl</artifactId>
<version>4.4.1</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20160212</version>
</dependency>
</dependencies>
<scm>
<connection>scm:git:ssh://git#xxx.com:7999/custom/ivrwrapper.git</connection>
<developerConnection>scm:git:ssh://git#xxx.com:7999/custom/ivrwrapper.git</developerConnection>
<tag>HEAD</tag>
</scm>
</project>
EDIT: based on #minus comment I have converted my JSON string to an XML string as below
JSONObject json = new JSONObject(JSONResponse.toString());
xml = XML.toString(json);
logger.info("GetCardInfo XML Response for KPN API: {} ", xml);
The log shows that it got converted to XML successfully.
<percentComplete>100</percentComplete><transactionId>FL2YTNR86KARMVYWWVK3410F4W</transactionId><card><product><productIdentifier>07675023660</productIdentifier><bin>451129</bin><name>EXM Visa Corp LAP</name><id>1000000000000000415</id><isActive>true</isActive><issuer>MetaBank</issuer></product><serviceCode>121</serviceCode><bin>451129</bin><isActive>false</isActive><proxyCardNumber>6039535101619469382</proxyCardNumber><accountStatusReason>NONE</accountStatusReason><accountStatus>OPEN</accountStatus><balances><pendingBalance>5000</pendingBalance><closingBalance>5000</closingBalance><openingBalance>5000</openingBalance><currencyCode>USD</currencyCode></balances><statusReason>NONE</statusReason><provisionType>PHYSICAL</provisionType><isRegistered>false</isRegistered><id>1000000000000098718</id><expirationDate>2017-06-30T23:59:59.000+0000</expirationDate><status>OPEN</status></card><isCompleted>true</isCompleted>
Next I am using below code to deserialize the XML String back to the Java object. But the deserialize is not working.
GetCardInfo gci = mapper.readValue(xml, GetCardInfo.class);
logger.info("Test12 ", gci.toString());
Now I don't get any error but Deserialization did not work. The fields in GCI object are null.
I'm not big at Jackson but you are trying to deserialize a json document with an XML mapper.
Jackson is telling you exactly that, you can't start an xml with '{'.
You should use a JsonMapper to deserialize Json and then an XMLMapper to serialize it.
I don't know if it is possible annotating the same class for both.
Thanks to #minus for his input.
I was able to figure out answer
all I needed was to add the classname to my XML string as below
String input = "" + xml + "";
After that I was able to deserialize successfully.
Related
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
#JsonIgnoreProperties(ignoreUnknown = true)
#JacksonXmlRootElement(localName = "Data")
public class Data {
#JacksonXmlProperty(localName="ServiceProviders")
private ServiceProviders serviceproviders;
public ServiceProviders getServiceproviders() {
return serviceproviders;
}
public void setServiceproviders(ServiceProviders serviceproviders) {
this.serviceproviders = serviceproviders;
}
public Data() {
super();
}
#Override
public String toString() {
return "Data [serviceproviders=" + serviceproviders + "]";
}
}
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
#JsonIgnoreProperties(ignoreUnknown = true)
#JacksonXmlRootElement(localName = "ServiceProviders")
public class ServiceProviders {
#JacksonXmlElementWrapper(localName="ServiceProvider")
#JsonProperty("ServiceProvider")
private List<ServiceProvider> serviceprovider;
public ServiceProviders() {
super();
}
public List<ServiceProvider> getServiceprovider() {
return serviceprovider;
}
public void setServiceprovider(List<ServiceProvider> serviceprovider) {
this.serviceprovider = serviceprovider;
}
#Override
public String toString() {
return "ServiceProviders [serviceprovider=" + serviceprovider + "]";
}
}
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
#JacksonXmlRootElement(localName = "ServiceProvider")
public class ServiceProvider {
#JacksonXmlProperty(localName = "ID")
#JsonProperty("ID")
private String id;
#JacksonXmlProperty(localName = "Name")
private String Name;
public ServiceProvider() {
super();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public ServiceProvider(String id, String name) {
super();
this.id = id;
Name = name;
}
#Override
public String toString() {
return "ServiceProvider [id=" + id + ", Name=" + Name + "]";
}
}
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
public class Parser {
public static void main(String[] args) throws IOException {
ObjectMapper objectMapper = new XmlMapper();
// Reads from XML and converts to POJO
Data data = objectMapper.readValue(
StringUtils.toEncodedString(Files.readAllBytes(Paths.get("C:\\Users\\d.roopa.gadiparthi\\Desktop\\sprint25\\CREtoJSON\\src\\main\\resources\\CRE.xml")), StandardCharsets.UTF_8),
Data.class);
System.out.println(data.getServiceproviders().getServiceprovider().get(1));
// ServiceProvider data1 = objectMapper.readValue(
// StringUtils.toEncodedString(Files.readAllBytes(Paths.get("C:\\Users\\d.roopa.gadiparthi\\Desktop\\sprint25\\CREtoJSON\\src\\main\\resources\\CRE.xml")), StandardCharsets.UTF_8),
// ServiceProvider.class);
// System.out.println(data1.getId()+data1.getName());
}
}
<Data>
<ServiceProviders>
<ServiceProvider ID="0" Name="0:Divya" />
<ServiceProvider ID="1" Name="1:Roopa" />
<ServiceProvider ID="2" Name="2:ransit" />
</ServiceProviders>
</Data>
dependecies are
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.11.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
</dependencies>
error
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index 1 out of bounds for length 0
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
at java.base/java.util.Objects.checkIndex(Objects.java:373)
at java.base/java.util.ArrayList.get(ArrayList.java:427)
at Parser.main(Parser.java:18)
The problem is in your ServiceProviders class.
There you have modeled the sequence of <ServiceProvider> elements by
#JacksonXmlElementWrapper(localName = "ServiceProvider")
#JsonProperty("ServiceProvider")
private List<ServiceProvider> serviceprovider;
That means, within <ServiceProviders>...</ServiceProviders>
you expect XML input like this
<ServiceProvider>
<ServiceProvider ID="0" Name="0:Divya" />
<ServiceProvider ID="1" Name="1:Roopa" />
<ServiceProvider ID="2" Name="2:ransit" />
</ServiceProvider>
instead of simply
<ServiceProvider ID="0" Name="0:Divya" />
<ServiceProvider ID="1" Name="1:Roopa" />
<ServiceProvider ID="2" Name="2:ransit" />
Of course you do not want the extra <ServiceProvider>...</ServiceProvider> wrapping.
Therefore you need to modify the #JacksonXmlElementWrapper annotation to simply
#JacksonXmlElementWrapper(useWrapping = false)
I am currently trying to extract the name 'Best I Ever Had' from the last.fm API shown below using GSON but having difficulty with it constantly returning a null value.
******************************EDIT*****************************
Here is the JSON, with tracks being a list of dictionaries, one for each song name:
{
toptracks: {
track: [
{
name: "Best I Ever Had"
}
]
}
}
Using http://www.jsonschema2pojo.org/ I have created the following classes:
TrackName.java
package com.webservice1;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class TrackName {
#SerializedName("toptracks")
#Expose
private Toptracks toptracks;
public Toptracks getToptracks() {
return toptracks;
}
public void setToptracks(Toptracks toptracks) {
this.toptracks = toptracks;
}
public TrackName withToptracks(Toptracks toptracks) {
this.toptracks = toptracks;
return this;
}
}
Toptracks.java
package com.webservice1;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Toptracks {
#SerializedName("track")
#Expose
private List<Track> track = null;
public List<Track> getTrack() {
return track;
}
public void setTrack(List<Track> track) {
this.track = track;
}
public Toptracks withTrack(List<Track> track) {
this.track = track;
return this;
}
}
and finally Track.java
package com.webservice1;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Track {
#SerializedName("name")
#Expose
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Track withName(String name) {
this.name = name;
return this;
}
}
And here is my code using fromJson method where reply contains the whole JSON and is constantly 'Track Name com.webservice1.Toptracks#1b9e1916'
String reply;
reply = reader.readLine();
Gson gson = new Gson();
TrackName response = gson.fromJson(reply, TrackName.class);
System.out.println("Track Name " + response.getToptracks());
Any help would be appreciated!!
You can do this using Jackson and I'm sure there are settings to ignore missing fields etc...
Here is the maven dependency for the library
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.2</version>
</dependency>
String json = "{\n"
+ " \"toptracks\" : {\n"
+ " \"track\": [{\n"
+ " \"name\": \"Best I Ever Had\",\n"
+ " \"playcount\": \"3551414\",\n"
+ " \"listeners\": \"1058277\",\n"
+ " \"mbid\": \"00bde944-7562-446f-ad0f-3d4bdc86b69f\",\n"
+ " \"url\": \"https://www.last.fm/music/Drake/_/Best+I+Ever+Had\",\n"
+ " \"streamable\": \"0\",\n"
+ " \"artist\": {\n"
+ " \"name\": \"Drake\",\n"
+ " \"mbid\": \"b49b81cc-d5b7-4bdd-aadb-385df8de69a6\",\n"
+ " \"url\": \"https://www.last.fm/music/Drake\"\n"
+ " }\n"
+ " }]\n"
+ "}\n"
+ "}";
ObjectMapper mapper = new ObjectMapper();
TopTracks track = mapper.readValue(json, TopTracks.class);
List<Object> trackList = (List<Object>)track.toptracks.get("track");
Map<String, Object> trackMap = (Map<String, Object>) trackList.get(0);
System.out.println(trackMap.get("name"));
public class TopTracks {
public Map<String, Object> toptracks = new HashMap<>();
}
Output:
--- exec-maven-plugin:1.2.1:exec (default-cli) # MVN ---
Best I Ever Had
------------------------------------------------------------------------
BUILD SUCCESS
I am new to JPA. Need to insert data to two databases tables(two tables are different that means no foreign key relationship).I am using POSTMAN to send json data from URL as well as payload body section.
Two entity classes are shown below.
USERREDIRECT.JAVA`
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.QueryHint;
import javax.persistence.Table;
import org.codehaus.jackson.annotate.JsonProperty;
#Entity
#Cacheable(false)
#NamedQueries({
#NamedQuery(name = "getuserRedirectById", query = "SELECT a FROM userRedirect a WHERE a.redirectId=:redirectId", hints = #QueryHint(name = "eclipselink.refresh", value = "true"))
,
#NamedQuery(name = "getAlluserRedirect", query = "SELECT a FROM userRedirect a order by a.redirectId", hints = #QueryHint(name = "eclipselink.refresh", value = "true"))})
#Table(name = "userRedirect")
public class userRedirect {
#Id
#Column(name = "redirect_id")
#JsonProperty("redirect_id")
private String redirectId;
#Column(name = "loop_ind")
#JsonProperty("loop_ind")
private String loopInd;
#OneToMany(mappedBy = "userRedirectEntity", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<userRedirectRicTgrpDetails> userRedirectRicTgrpDetailsList;
public userRedirect() {
}
public String getLoopInd() {
return loopInd;
}
public void setLoopInd(String loopInd) {
this.loopInd = loopInd;
}
public List<userRedirectRicTgrpDetails> getuserRedirectRicTgrpDetailsList() {
return userRedirectRicTgrpDetailsList;
}
public void setuserRedirectRicTgrpDetailsList(List<userRedirectRicTgrpDetails> userRedirectRicTgrpDetailsList) {
this.userRedirectRicTgrpDetailsList = userRedirectRicTgrpDetailsList;
}
/**
* #return the redirectId
*/
public String getRedirectId() {
return redirectId;
}
/**
* #param redirectId the redirectId to set
*/
public void setRedirectId(String redirectId) {
this.redirectId = redirectId;
}
#Override
public String toString() {
return "userRedirect :: RedirectId: " + redirectId + ", LoopInd : "
+ loopInd + ", Ric_Tgrp Details : " + userRedirectRicTgrpDetailsList;
}
}
userRedirectRicTgrpDetails.java
import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.QueryHint;
import javax.persistence.Table;
import org.codehaus.jackson.annotate.JsonProperty;
#Entity
#Cacheable(false)
#NamedQueries({
#NamedQuery(name = "getuserRedirectDetailsById", query = "SELECT a FROM userRedirectRicTgrpDetails a WHERE a.userRedirectEntity.redirectId=:redirectId ORDER BY a.priority", hints = #QueryHint(name = "eclipselink.refresh", value = "true"))
,
#NamedQuery(name = "getAlluserRedirectDetails", query = "SELECT a FROM userRedirectRicTgrpDetails a ORDER BY a.priority", hints = #QueryHint(name = "eclipselink.refresh", value = "true"))})
#Table(name = "userRedirect_ric_tgrp_details")
public class userRedirectRicTgrpDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "row_id")
private int id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "redirect_id")
private userRedirect userRedirectEntity;
#Column(name = "ric")
#JsonProperty("ric")
private String ric;
#Column(name = "tgrp")
#JsonProperty("tgrp")
private String tgrp;
#Column(name = "priority")
#JsonProperty("priority")
private int priority;
public userRedirectRicTgrpDetails() {
}
public userRedirect getuserRedirect() {
return userRedirectEntity;
}
public void setuserRedirect(userRedirect userRedirect) {
this.userRedirectEntity = userRedirect;
}
/**
* #return the ric
*/
public String getRic() {
return ric;
}
/**
* #param ric the ric to set
*/
public void setRic(String ric) {
this.ric = ric;
}
/**
* #return the tgrp
*/
public String getTgrp() {
return tgrp;
}
/**
* #param tgrp the tgrp to set
*/
public void setTgrp(String tgrp) {
this.tgrp = tgrp;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
#Override
public String toString() {
return "userRedirectRicTgrpDetails ::RowId : " + id + ", RedirectId : " + userRedirectEntity.getRedirectId() + ", Ric : " + ric + ", Tgrp : " + tgrp
+ ", Priority : " + priority;
}
}
Exception:
SEVERE: Unexpected error while creating new RedirectGroup Exception =
java.lang.IllegalArgumentException: Object: callRedirect :: RedirectId: 1000, Lo
opInd : ORIG, Ric_Tgrp Details : [callRedirectRicTgrpDetails ::RowId : 0, Redire
ctId : 1000, Ric : RICXXX3, Tgrp : TGRPXXXX3, Priority : 1] is not a known entit
y type.
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.registerNewO
bjectForPersist(UnitOfWorkImpl.java:4184)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.persist(Entity
ManagerImpl.java:368)
Database schema:
CREATE TABLE IF NOT EXISTS `userRedirect` (
`redirect_id` varchar(20) NOT NULL,
`loop_ind` varchar(4) NOT NULL,
PRIMARY KEY (`redirect_id`)
);
CREATE TABLE IF NOT EXISTS `userRedirect_ric_tgrp_details` (
`row_id` int(4) AUTO_INCREMENT,
`redirect_id` varchar(20) NOT NULL,
`ric` varchar(20) NOT NULL,
`tgrp` varchar (20) NOT NULL,
`priority` int NOT NULL,
PRIMARY KEY (`row_id`)
);
JSON INPUT:
{
"loop_ind": "ORIG",
"callRedirectRicTgrpDetailsList": [{
"ric": "RICXXX3",
"tgrp": "TGRPXXXX3",
"priority": 1
}]
}
and redirect_id is given from URL
Persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="TEST_CALLREDIRECT_PERSISTENCE" transaction-type="RESOURCE_LOCAL">
<class>com.mypackage.userRedirect</class>
<class>com.mypackage.userRedirectRicTgrpDetails</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/CallRedirect" />
<property name="javax.persistence.jdbc.user" value="root" />
<property name="javax.persistence.jdbc.password" value="password" />
</properties>
</persistence-unit>
</persistence>
DAO.Class:
public userRedirect createNewuserRedirect(userRedirect userRedirect){
try {
System.out.println("inside dao.....");
LOGGER.fine("createNewRedirectGroup method invoked in dao with userRedirect : " + userRedirect);
entityManager.getTransaction().begin();
entityManager.persist(userRedirect);
entityManager.getTransaction().commit();
return userRedirect;
} catch (Exception e) {
System.out.println(e);
}
}
I cannot create Java Getters and Setters, because I got number(digit) for my Object Key.
I will show you my API response. How can I achieve this without changing the API.
{"api_status": true,
"message": "",
"data": {
"0": {
"id": "aaa",
"name": "aaa",
"address": "aaa",
"category": "aaa",
"open_24_hours": "aaa",
"business_open": "",
"business_close": "",
"type": "0",
"title": null,
"latitude": "6.8729428",
"longitude": "79.8689013",
"city": "",
"distance": "2.95555089735992"
},
"1": {
"id": "bbb",
"name": "bbb",
"address": "bbb",
"category": "bbb",
"open_24_hours": "bbb",
"business_open": "",
"business_close": "",
"type": "0",
"title": null,
"latitude": "6.8767581",
"longitude": "79.8674747",
"city": "",
"distance": "2.915385898910569"
},
}
}
Use the below class and pass it to GSON library with your json data and the Class As a model . you will get your model, each data item is mapped with hashtable where key is your number which i represent as string By iterating over hash map you will get keySet which is your all keys in the data key of json. and for each key you can get itemData.
class JsonStructure{
public boolean api_status;
public String message
HashMap<String,ItemsData> data;
}
class ItemsData{
public String id;
public String name;
public String address;
public String category;
public String open_24_hours;
public String business_open;
public String business_close;
public String type;
public String title;
public String latitude;
public String longitude;
public String city;
public String distance;
}
For retrofit Build
BuildRetrofit(){
mOkHttpClient = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
mConverterFactory = GsonConverterFactory.create();
String baseUrl = "http://dev.appslanka.com/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.client(mOkHttpClient)
.addConverterFactory(mConverterFactory)
.build();
mApi = retrofit.create(ApiInterface.class);
}
In ApiInterface define yoyr request method
interface ApiInterface{
#GET("_test/placeInDistance/")
Call<JsonStructure> getResponseForApiCall();
}
Now call this method as retrofit call structure:
Call<JsonStructure> call = mApi.getResponseForApiCall();
Response<JsonStructure> response = call.execute();
Parse this response like below:
HashMap<String, ItemsData> map = response .data;
Set<String> s = map.keySet();
Iterator<String> i = s.iterator();
while (i.hasNext()){
String key = i.next();
ItemsData data = map.get(key);
String id = data.id;
String name = data.name;
String address = data.address;
String category = data.category;
String open24Hr = data.open_24_hours;
String businessOpen = data.business_open;
String close = data.business_close;
String latitue = data.latitude;
..... etc
}
Yes, you can. Use SerializedName annotation like this:
#SerializedName("0")
private MyClass myObject;
Where MyClass is gonna represent a POJO for the data you're getting back.
I just want to note that a better solution would be to change the API (cause this response is weird), to return a list rather than an object with digits for keys, but I can see that you wrote in the question that you cannot change it.
If you really need to parse this JSON. Use custom solution.
For example my solution.
Create class Response with following code :
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
public class Response {
public boolean apiStatus;
public String message;
public List<Data> datas;
public Response(JSONObject jsonObject) {
apiStatus = jsonObject.optBoolean("api_status");
message = jsonObject.optString("message");
datas = new ArrayList<>();
try {
JSONObject datasJSON = jsonObject.getJSONObject("data");
int index = 0;
while (datasJSON.has(String.valueOf(index))) {
JSONObject dataJSON = datasJSON.getJSONObject(String.valueOf(index));
datas.add(new Data(dataJSON));
index++;
}
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override public String toString() {
return "Response{" +
"apiStatus=" + apiStatus +
", message='" + message + '\'' +
", datas=" + datas +
'}';
}
}
Create class Data with following code :
import org.json.JSONObject;
public class Data {
public String id;
public String name;
public String address;
public String category;
public String open24Hours;
public String businessOpen;
public String businessClose;
public String type;
public String title;
public String latitude;
public String longitude;
public String city;
public String distance;
public Data(JSONObject jsonObject) {
id = jsonObject.optString("id");
name = jsonObject.optString("name");
address = jsonObject.optString("address");
category = jsonObject.optString("category");
open24Hours = jsonObject.optString("open_24_hours");
businessOpen = jsonObject.optString("business_open");
businessClose = jsonObject.optString("business_close");
type = jsonObject.optString("type");
title = jsonObject.optString("title");
latitude = jsonObject.optString("latitude");
longitude = jsonObject.optString("longitude");
city = jsonObject.optString("city");
distance = jsonObject.optString("distance");
}
#Override public String toString() {
return "Data{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", address='" + address + '\'' +
", category='" + category + '\'' +
", open24Hours='" + open24Hours + '\'' +
", businessOpen='" + businessOpen + '\'' +
", businessClose='" + businessClose + '\'' +
", type='" + type + '\'' +
", title='" + title + '\'' +
", latitude='" + latitude + '\'' +
", longitude='" + longitude + '\'' +
", city='" + city + '\'' +
", distance='" + distance + '\'' +
'}';
}
}
Instruction for use this solution:
Response response = new Response(jsonObject);
Instruction for use it, when you use Retrofit2.
For first we need to create custom factory, create class with name ResponseRetrofitConverter, and this following code :
import android.support.annotation.NonNull;
import org.json.JSONObject;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;
public class ResponseRetrofitConverter extends Converter.Factory {
public static ResponseRetrofitConverter create() {
return new ResponseRetrofitConverter();
}
#Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return new JsonConverter();
}
private final static class JsonConverter implements Converter<ResponseBody, Response> {
#Override
public Response convert(#NonNull ResponseBody responseBody) {
try {
return new Response(new JSONObject(responseBody.string()));
} catch (Exception e) {
return null;
}
}
}
}
When Response is your entity,
Add connect with factory to retrofit use following code line :
.addConverterFactory(ResponseRetrofitConverter.create())
For example my code:
Retrofit.Builder()
.baseUrl(link)
.addConverterFactory(ResponseRetrofitConverter.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
You should create a java List of objects to represent the data.
If you want to bind a Json that has a number as name, and if you are using jackson as json library, you can declare the variable as follow:
#JsonProperty("0")
private CustomObject zero;
#JsonProperty("1")
private CustomObject one;
public CustomObject getZero()
{
return this.zero;
}
public void setZero(CustomObject zero)
{
this.zero= zero;
}
public CustomObject getOne()
{
return this.one;
}
public void setOne(CustomObject one)
{
this.one= one;
}
If you are using Gson then you can use as follows:
public class Model{
#SerializedName("0")
private String object;
}
You can call you class _0, _1... even it's a little bit strange.
I'm trying to deserialize this XML into a Parts object:
<Parts>
<Part>
<Name>gearbox</Name>
<Year>1990</Year>
</Part>
<Part>
<Name>wheel</Name>
<Year>2000</Year>
</Part>
</Parts>
Car.java:
package problem.car;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Car {
public static void main(String args[]) {
try {
String xml = "<Parts>\n"
+ " <Part>\n"
+ " <Name>gearbox</Name>\n"
+ " <Year>1990</Year>\n"
+ " </Part>\n"
+ " <Part>\n"
+ " <Name>wheel</Name>\n"
+ " <Year>2000</Year>\n"
+ " </Part>\n"
+ "</Parts>";
Parts parts = (Parts) deserialize(Parts.class, xml);
} catch (IOException ex) {
Logger.getLogger(Car.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static final Object deserialize(final Class clazz, final String xml) throws IOException {
ObjectMapper xmlMapper = new XmlMapper();
xmlMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz"));
Object object;
try {
object = xmlMapper.readValue(xml, clazz);
} catch (com.fasterxml.jackson.databind.exc.InvalidFormatException ex) {
xmlMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
object = xmlMapper.readValue(xml, clazz);
}
return object;
}
}
Parts.java:
package problem.car;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import java.util.ArrayList;
import java.util.List;
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"Part"
})
public class Parts {
#JsonProperty("Part")
private List<Part> Part = new ArrayList<>();
#JsonProperty("Part")
public List<Part> getPart() {
return Part;
}
#JsonProperty("Part")
public void setPart(List<Part> Part) {
this.Part = Part;
}
}
Part.java:
package problem.car;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"Name",
"Year"
})
public class Part {
#JsonProperty("Name")
private String Name;
#JsonProperty("Year")
private String Year;
#JsonProperty("Name")
public String getName() {
return Name;
}
#JsonProperty("Name")
public void setName(String Name) {
this.Name = Name;
}
#JsonProperty("Year")
public String getYear() {
return Year;
}
#JsonProperty("Year")
public void setYear(String Year) {
this.Year = Year;
}
}
I don't see anything wrong with my code though so why does it keep giving me the following?
com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [simple type, class problem.car.Part] from String value ('gearbox'); no single-String constructor/factory method
at [Source: java.io.StringReader#598067a5; line: 3, column: 28] (through reference chain: problem.car.Parts["Part"]->java.util.ArrayList[0])
You need to disable use of "wrapped" lists, to match the structure.
This should be explained on README page of xml dataformat github project page.