jax-rs Jersey #Post Response entity is empty - java

I'm trying to #POST a user-created object and get a Response with a different user-created payload as the entity. Although the object returned exists and is populated, on the client end it is empty.
Client sent / server received object:
#XmlRootElement
public class TweetQuery {
String query;
List<TweetQueryTweet> tweets = new ArrayList<>();
// setters and getters
}
public class TweetQueryTweet {
private String id;
private String text;
// setters and getters
}
Server received / client sent object:
#XmlRootElement
public class TweetClusters {
List<TweetCluster> tweetClusters = new ArrayList<>();
// setters and getters
}
public class TweetCluster {
List<String> labels = new ArrayList<>();
List<String> docs = new ArrayList<>();
// setters and getters
}
Client (Arquillian) Test:
#Test
#RunAsClient
public void test01SeeSomething(#ArquillianResource URL deploymentUrl) throws ... {
final URI targetURI = ...;
System.out.println(" test target:" + targetURI.toASCIIString());
Entity<TweetQuery> tweetQuery = Entity.entity(getTestTweetQuery(), MediaType.APPLICATION_JSON);
Client client = ClientBuilder.newBuilder().build();
WebTarget target = client.target(targetURI.toASCIIString());
Response response = target.request(MediaType.APPLICATION_JSON).post(tweetQuery);
TweetClusters x = response.readEntity(TweetClusters.class);
System.out.println("Entity:" + x);
System.out.println("Response: " + response.getStatus());
assertEquals(Status.OK.getStatusCode(), response.getStatus());
assertNotNull(x);
assertThat(x.getTweetClusters().size()).isGreaterThan(0);
}
Jersey Post method:
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response clusterPost(TweetQuery tweetQuery) {
TweetClusters tweetClusters = clusterService.getTweetClusters(tweetQuery);
System.out.println("clusterPost - in - tweetQuery: " + tweetQuery);
System.out.println(" - out tweetClusters: " + tweetClusters);
return Response.status(Status.OK).type(MediaType.APPLICATION_JSON).entity(tweetClusters).build();
}
Debug:
test target:http://localhost:18080/test//cluster
clusterPost - in - tweetQuery: [TweetQuery - query:TweetQuery query, tweets:[[TweetQueryTweet - id:1, text:text 1], [TweetQueryTweet - id:2, text:text 2], [TweetQueryTweet - id:3, text:text 3]]]
- out tweetClusters: [TweetClusters:[[TweetCluster - labels: [Other Topics], docs:[3, 2, 1]]]]
Entity:[TweetClusters:[]]
Response: 200
Line 2 - clusterPost - in -- shows TweetQuery is being marshalled properly.
Line 3 - clusterPost - out -- shows the tweetClusters to be sent as the Response entity exists
Line 4 - tweetClusters is not coming out of the request
Edit
I changed the REST method to return the tweetQuery that it receives as input and it is returned correctly. So its something about TweetClusters. Maybe I need a MessageBodyReader & Writer. Or a Moxy #XmlJavaTypeAdapter. But for what as Lists obviously work out of the box as TweetQuery works.
https://stackoverflow.com/users/383861/blaise-doughan are you out there? :)
Am I missing something simple?

oK I worked it out. I unintentionally told a porkie-pie. When I said I had setters and getters I was mising the setter for TweetClusters. Then once fixed i had a constructor with an argument but no no-arg constructor. Once the no-arg constructor added it was all good.
In summary you need to have in objects to be (un)marshalled:
A no-arg constructor if you have an arg constructor
Setters and getters for all the elements
And if you have more complex types including Date and Calendar you need to have a Adapter #XmlJavaTypeAdapter (Moxy) or #JsonSerialize.using in Jackson (or ??? in RESTeasy ...).
Interestingly I didn't need to have #XmlRootElement (Moxy) though kept it there for good measure.
The complete answer is:
The Client (Arquillian) Test is same as above
The Jersey Post method is same as above
The object classes that (un)marshall are:
#XmlRootElement
public class TweetClusters {
List tweetClusters = new ArrayList<>();
public void addCluster(Cluster c) {
TweetCluster tweetCluster = new TweetCluster(c);
tweetClusters.add(tweetCluster);
}
public List<TweetCluster> getTweetClusters() {
return tweetClusters;
}
public void setTweetClusters(List<TweetCluster> tweetClusters) {
this.tweetClusters = tweetClusters;
}
#Override
public String toString() {
return String.format("[TweetClusters:%s]", tweetClusters);
}
}
public class TweetCluster {
List labels = new ArrayList<>();
List docs = new ArrayList<>();
public TweetCluster() {
}
public TweetCluster(Cluster c) {
labels.add(c.getLabel());
for (Document doc : c.getDocuments()) {
docs.add(doc.getTitle());
}
}
public List<String> getLabels() {
return labels;
}
public void setLabels(List<String> labels) {
this.labels = labels;
}
public List<String> getDocs() {
return docs;
}
public void setDocs(List<String> docs) {
this.docs = docs;
}
#Override
public String toString() {
return String.format("[TweetCluster - labels: %s, docs:%s]", labels, docs);
}
}
public class TweetQuery {
String query;
List<TweetQueryTweet> tweets = new ArrayList<>();
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
public List<TweetQueryTweet> getTweets() {
return tweets;
}
public void setTweets(List<TweetQueryTweet> tweets) {
this.tweets = tweets;
}
public void addTweets(TweetQueryTweet... queryTweets) {
for (TweetQueryTweet tweet : queryTweets) {
this.tweets.add(tweet);
}
}
#Override
public String toString() {
return String.format("[TweetQuery - query:%s, tweets:%s]",query, tweets);
}
}
(Arghhh, sorry about the formatting; I can't fix it in SO)
For debugging purposes it is often good to get back the string representation returned from the response (ie. XML or JSON) and you simply specify the entity type as String.class:
String x = response.readEntity(String.class);

Try to return response like this : Response.ok(tweetClusters).build();

I'm not sure why you have the models Annotated with #XmlRootElements. So I would think you could remove that and make sure you are using Jackson to Serialize and Deserialize your request and response body. Which I assume you are or gson.

Related

GET more than one object from JSON using Mono

Looking at this code:
#Service
public class WebService {
public WebService() {
this.webClient = WebClient.builder()
.baseUrl(URL)
.build();
}
public Mono<Person> searchById(String id) {
return webClient.get().uri("/v3/"+ id).retrieve().bodyToMono(Person.class);
}
}
And this:
WebService ws = new WebService();
Mono<Person> version = ws.searchById("1");
System.out.println(version.toProcessor().block());
This code works well in getting one person object from a JSON, however I wanted a method to return more than one person from a JSON like so:
public Mono<List<Person>> or public Mono<Person[]>
And I can't seem to make it work. I've tried what I found here, but I don't understand what they are doing here:
return Arrays.stream(objects)
.map(object -> mapper.convertValue(object, Reader.class))
.map(Reader::getFavouriteBook)
.collect(Collectors.toList());
And in my case I want to have a list of objects Person on my main function, not just part of the object like in their case, where they use Reader::getFavouriteBook to apparently map a Book.
Converting this to an answer, for better readability.
WebClient can convert the response body into a mono of array. So, instead of performing bodyToMono(Person.class) we can do bodyToMono(Person[].class).
Now the resulting Mono is a Mono<Person[]>. You can then perform map, subscribe or flatMap as you wish (one or many times) to chain actions. Once all the actions are chained, you can then call block to wait till all the actions are executed and get back the result (or an error).
See this documentation to know more about all the supported methods on Mono.
Note - Using block in a reactive application is not recommended.
EDIT
Here is an example on how a Mono<Person> is converted to a Mono<PersonDTO>
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
public class Test {
public static void main(String[] args) {
Person person = new Person();
Mono<Person> personMono = Mono.just(person);
Mono<PersonDTO> personDTOMono = personMono.map(p -> {
PersonDTO dto = new PersonDTO();
dto.setName(p.name);
dto.setVersion(p.version);
return dto;
}).delayElement(Duration.of(1000L, ChronoUnit.MILLIS));
System.out.println("Waiting for 1000 millis");
System.out.println("personDTOMono = " + personDTOMono.block());
}
}
class Person {
int version = 10;
String name = "test";
}
class PersonDTO {
private int version;
private String name;
#Override
public String toString() {
return "PersonDTO{" +
"version=" + version +
", name='" + name + '\'' +
'}';
}
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

Java. Cast generic List to list of objects from factory

Is it possible to cast List Objects to List of Objects from Factory pattern?
I have a Jersey REST endpoint and I migrate data from one environment to another. I wish to post some list of Object and cast them to right object taken from factory pattern
#Path("/migrateTableAtOnce")
#Consumes(MediaType.APPLICATION_JSON)
public <T> Response saveObjectIntoDb(List<T> listOfObj) {
// if listOfObj.getTableName() == "MW_ID_GENERATOR" tableOject gets new MwIdGenerator()
myEntity tableObject = myEntityFactory.getTable(listOfObj.getTableName());
return Response.status(201).entity("ok").build();
}
Is it possible that I have only one post method which that generic List. I have 20 objects which I need to transfer and I dont want to write 20 post methods :( I dont know how to do it exactly.
One of my method looks like that:
#POST
#Path("/migrateTableAtOnceMwIdGenerator")
#Consumes(MediaType.APPLICATION_JSON)
public Response saveObjectIntoDb(List<MwIdGenerator> listOfObj) {
Boolean result = false;
String dbResponse ="";
try {
dbResponse = obtainFacade().saveToDb(listOfObj);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return Response.status(201).entity(result+" "+dbResponse).build();
}
Is there no better solution to solve this problem?
I post the body as a custom object, and have a list within that object like;
#POST
#Path("/migrateTableAtOnceMwIdGenerator")
#Consumes(MediaType.APPLICATION_JSON)
public Response saveObjectIntoDb(CustomObject object) {
List <Stuff>list = object.getList();
and the object
public class CustomObject extends Serializable {
public List <Stuff>sList = null;
public List <OtherStuff>osList = null;
public List <TheBestStuff>tbsList = null; //etcetc
public List getList ()
{
return list;
}
public void setList(List <Stuff>list)
{
this.list = list;
}
public List getOsList ()
{
return osList ;
}
public void setList(List <OtherStuff>osList)
{
this.osList = osList;
}
public List getTbsList ()
{
return tbsList;
}
public void setTbsList(List <TheBestStuff>tbsList)
{
this.tbsList = tbsList;
}
and Jersey can parse the json object into your custom object, provided that you can pass the fields of that class as json. Seeing as you're passing the data with json, you're limited as to your implimentation as json only covers String, int, boolean and simple date, but you can pass most values as String and then parse.

Write RequestBody for Restful method

I'm a new member in Restful API, I'm writing a GET method:
#RequestMapping(method = RequestMethod.GET, value = "/resourcerecords", produces={"application/json", "application/xml"})
public #ResponseBody Object getRRs(#RequestBody RRRequest requestBody){
// do something
}
The RRRequest class:
public class RRRequest{
private RRREC reqObject;
// getter and setter
}
The RRREC class:
public class RRREC{
protected String infraAddr;
protected RRINFRATYPE infraType;
// getter and setter
}
And the RRINFRATYPE class:
public enum RRINFRATYPE {
V_6_ADDRESS("V6ADDRESS"),
OBJECT("OBJECT"),
ZONE("ZONE"),
V_4_REVERSEZONE("V4REVERSEZONE"),
V_6_REVERSEZONE("V6REVERSEZONE"),
NODE("NODE"),
ALL("ALL");
private final String value;
RRINFRATYPE(String v) {
value = v;
}
public String value() {
return value;
}
public static RRINFRATYPE fromValue(String v) {
for (RRINFRATYPE c: RRINFRATYPE.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}
}
Then, I sent a request GET with RequestBody ( I use Fiddler Web Debugger)
"reqObject" : {
"infraAddr" : "192.168.88.4",
"infraType": {
"value": "OBJECT"
}
}
I get 400 Bad Request. If I change to
"reqObject" : {
"infraAddr" : "192.168.88.4",
"InfraType": {
"value": "OBJECT"
}
}
I can debug.
However, The reqObject only receive infraAddr with "192.168.88.4", the InfraType is null.
Who can explain to me, why I must be use "InfraType" instead of "infraType" and how to send value for InfraType.
The first one is when your api in GET method you still cant send body of request to server, try to change it to POST.
Because you use ENUM in your Object so you should define a converter like Converting JSON to Enum type with #RequestBody
But in this case, I think the fastest way is change infraType to String and use switch case with String on server side.
public class RRREC{
protected String infraAddr;
protected String infraType;
// getter and setter
}
Your JSON will be:
{
"reqObject" : {
"infraAddr" : "192.168.88.4",
"infraType": "OBJECT"
}
}

Parsing Json File using Jackson

{
"TestSuite":{
"TestSuiteInfo":{
"-description":"parse"
},
"TestCase":[
{
"TestCaseData":{
"-sequence":"sequential",
"-testNumber":"2",
"-testCaseFile":"testcase\\Web\\Ab.xml"
}
},
{
"TestCaseData":{
"-sequence":"sequential",
"-testNumber":"3",
"-testCaseFile":"testcase\\Web\\BC.xml"
}
}
]
}
}
My Pojos are:
public class TestSuite {
private TestSuiteInfo testSuiteInfo;
private TestCase listOfTestCases;
public TestSuiteInfo getTestSuiteInfo() {
return testSuiteInfo;
}
public void setTestSuiteInfo(TestSuiteInfo testSuiteInfo) {
this.testSuiteInfo = testSuiteInfo;
}
public TestCase getListOfTestCases() {
return listOfTestCases;
}
public void setListOfTestCases(TestCase listOfTestCases) {
this.listOfTestCases = listOfTestCases;
}
}
public class TestSuiteInfo {
private String description;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
import java.util.Iterator;
import java.util.List;
public class TestCase {
private List<TestCaseData> testCaseData;
public List<TestCaseData> getTestCaseData() {
return testCaseData;
}
public void setTestCaseData(List<TestCaseData> testCaseData) {
this.testCaseData = testCaseData;
}
}
public class TestCaseData {
private String sequence;
private int testNumber;
private String testCaseFile;
public String getSequence() {
return sequence;
}
public void setSequence(String sequence) {
this.sequence = sequence;
}
public int getTestNumber() {
return testNumber;
}
public void setTestNumber(int testNumber) {
this.testNumber = testNumber;
}
public String getTestCaseFile() {
return testCaseFile;
}
public void setTestCaseFile(String testCaseFile) {
this.testCaseFile = testCaseFile;
}
}
I haven't use Jackson before, will really appreciate if anyone could help me in parsing the file and getting the objects.
I am trying to parse this from past two days, but didnt got any success
Usually to parse JSON with the Jackson library, you would use the ObjectMapper class like this:
public static void main(final String[] args) {
final String json = "some JSON string";
final ObjectMapper mapper = new ObjectMapper();
final TestSuite readValue = mapper.readValue(json, TestSuite.class);
//Then some code that uses the readValue.
//Keep in mind that the mapper.readValue() method does throw some exceptions
//So you'll need to handle those too.
}
However, I wrote a quick test class to check out the parsing of your JSON and came across some issues.
Basically, the design of the JSON and the design of the domain don't match up. So you can either alter the JSON, or you can alter the domain objects.
Altering the JSON to fit the domain
The property names that have "-" in them wont parse nicely in jackson, so they will need to be removed.
Having the class name before eachof the objects isn't going to help. Jackson will expect these to be properties, so the Class names will need removing or replacing with property names.
Property names must be provided as they are in the domain objects in order for jackson to parse them. You can't just say here's an object and then start a list, the list must have a property name/
After I'd adjusted a these things in the JSON, I got it to parse with the provided domain objects. The JSON I ended up with looked like this:
{
"testSuiteInfo":{
"description":"parse"
},
"listOfTestCases":{
"testCaseData":[
{
"sequence":"sequential",
"testNumber":"2",
"testCaseFile":"testcase\\Web\\Ab.xml"
},
{
"sequence":"sequential",
"testNumber":"3",
"testCaseFile":"testcase\\Web\\BC.xml"
}
]
}
}
Here's my test method that does parse the doctored JSON above (please ignore all the escape characters)
public static void main(final String[] args) {
final String json = "{\"testSuiteInfo\":{\"description\":\"parse\"}," +
"\"listOfTestCases\":{" +
"\"testCaseData\":[" +
"{\"sequence\":\"sequential\",\"testNumber\":\"2\",\"testCaseFile\":\"testcase\\\\Web\\\\Ab.xml\"}," +
"{\"sequence\":\"sequential\",\"testNumber\":\"3\",\"testCaseFile\":\"testcase\\\\Web\\\\BC.xml\"}" +
"]" +
"}" +
"}";
final ObjectMapper mapper = new ObjectMapper();
try {
final TestSuite readValue = mapper.readValue(json, TestSuite.class);
System.out.println(readValue.getListOfTestCases()); //just a test to see if the object is built
}
catch (final Exception e) {
e.printStackTrace();
}
}
Altering the domain to fit the JSON
Firstly, the main issues is having the Class names as the property identifiers. That makes it quite difficult to work with this JSON in the usual manner. I've had to add a couple of wrapper classes to get around the class names being in the JSON.
I've added an OverallWrapper class that has a TestSuite property to cater for the TestSuite class name in the JSON.
I've also added a TestCaseDataWrapper class to cater for the TestCaseData class names in the list in the JSON.
I removed the TestCase class all together as that just became a property on one of the other classes.
Then to make the property names match up with the objects, I've used the #JsonProperty annotation.
Here are the classes after the modifications, and the ultimate parser test method that works and parses the JSON. (again, excuse all the escape characters in the JSON string)
import org.codehaus.jackson.annotate.JsonProperty;
public class OverallWrapper {
private TestSuite testSuite;
#JsonProperty("TestSuite")
public TestSuite getTestSuite() {
return this.testSuite;
}
public void setTestSuite(final TestSuite testSuite) {
this.testSuite = testSuite;
}
}
import java.util.List;
import org.codehaus.jackson.annotate.JsonProperty;
public class TestSuite {
private TestSuiteInfo testSuiteInfo;
private List<TestCaseDataWrapper> testCaseData;
#JsonProperty("TestCase")
public List<TestCaseDataWrapper> getTestCaseData() {
return this.testCaseData;
}
public void setTestCaseData(final List<TestCaseDataWrapper> testCaseData) {
this.testCaseData = testCaseData;
}
#JsonProperty("TestSuiteInfo")
public TestSuiteInfo getTestSuiteInfo() {
return this.testSuiteInfo;
}
public void setTestSuiteInfo(final TestSuiteInfo testSuiteInfo) {
this.testSuiteInfo = testSuiteInfo;
}
}
import org.codehaus.jackson.annotate.JsonProperty;
public class TestSuiteInfo {
private String description;
#JsonProperty("-description")
public String getDescription() {
return this.description;
}
public void setDescription(final String description) {
this.description = description;
}
}
import org.codehaus.jackson.annotate.JsonProperty;
public class TestCaseDataWrapper {
#JsonProperty("TestCaseData")
private TestCaseData testcaseData;
public TestCaseData getTestcaseData() {
return this.testcaseData;
}
public void setTestcaseData(final TestCaseData testcaseData) {
this.testcaseData = testcaseData;
}
}
import org.codehaus.jackson.annotate.JsonProperty;
public class TestCaseData {
private String sequence;
private int testNumber;
private String testCaseFile;
#JsonProperty("-sequence")
public String getSequence() {
return this.sequence;
}
public void setSequence(final String sequence) {
this.sequence = sequence;
}
#JsonProperty("-testNumber")
public int getTestNumber() {
return this.testNumber;
}
public void setTestNumber(final int testNumber) {
this.testNumber = testNumber;
}
#JsonProperty("-testCaseFile")
public String getTestCaseFile() {
return this.testCaseFile;
}
public void setTestCaseFile(final String testCaseFile) {
this.testCaseFile = testCaseFile;
}
}
public static void main(final String[] args) {
final String json = "{\"TestSuite\":{\"TestSuiteInfo\":{\"-description\":\"parse\"},\"TestCase\":[" +
"{\"TestCaseData\":{\"-sequence\":\"sequential\",\"-testNumber\":\"2\",\"-testCaseFile\":\"testcase\\\\Web\\\\Ab.xml\"}}," +
"{\"TestCaseData\":{\"-sequence\":\"sequential\",\"-testNumber\":\"3\",\"-testCaseFile\":\"testcase\\\\Web\\\\BC.xml\"}}" +
"]}}";
final ObjectMapper mapper = new ObjectMapper();
try {
final OverallWrapper readValue = mapper.readValue(json, OverallWrapper.class);
System.out.println(readValue.getTestSuite());
}
catch (final Exception e) {
e.printStackTrace();
}
}
Summing up
The ultimate issue is that the domain doesn't marry up with the JSON.
Personally I prefer to change the JSON to marry up to the domain, as the domain seems to make sense in it's design and requires less customization and forcing.
However, I do accept that you may not have that choice, hence the redesign of the domain.
In this blog you can find a simple way to parse a large json file without directly using Jackson's ObjectMapper
https://www.ngdata.com/parsing-a-large-json-file-efficiently-and-easily/
With jp.skipChildren() and nested loops you can reach to your section of interest and once you are there simply break the nested loops using a label:
outerloop: while (jp.nextToken() != JsonToken.END_OBJECT) {
//...nested loops here
break outerloop;
//...closing loops
}
I copied the code for reference:
import org.codehaus.jackson.map.*;
import org.codehaus.jackson.*;
import java.io.File;
public class ParseJsonSample {
public static void main(String[] args) throws Exception {
JsonFactory f = new MappingJsonFactory();
JsonParser jp = f.createJsonParser(new File(args[0]));
JsonToken current;
current = jp.nextToken();
if (current != JsonToken.START_OBJECT) {
System.out.println("Error: root should be object: quiting.");
return;
}
while (jp.nextToken() != JsonToken.END_OBJECT) {
String fieldName = jp.getCurrentName();
// move from field name to field value
current = jp.nextToken();
if (fieldName.equals("records")) {
if (current == JsonToken.START_ARRAY) {
// For each of the records in the array
while (jp.nextToken() != JsonToken.END_ARRAY) {
// read the record into a tree model,
// this moves the parsing position to the end of it
JsonNode node = jp.readValueAsTree();
// And now we have random access to everything in the object
System.out.println("field1: " + node.get("field1").getValueAsText());
System.out.println("field2: " + node.get("field2").getValueAsText());
}
} else {
System.out.println("Error: records should be an array: skipping.");
jp.skipChildren();
}
} else {
System.out.println("Unprocessed property: " + fieldName);
jp.skipChildren();
}
}
}
}
From the blog:
The nextToken() call each time gives the next parsing event: start object, start field, start array, start object, …, end object, …, end array, …
The jp.skipChildren() is convenient: it allows to skip over a complete object tree or an array without having to run yourself over all the events contained in it.
All the credits go to the blog's author: Molly Galetto

Java List of custom objects inside response object not populated with GSON unmarshaller

Basically, I marshall on one server into JSON, then send it to another server, where it should be unmarshalled. I use a response object called list wrapper, so that if there are any errors I can pass them along. With the JSON marshalled below by badgerfish(jettison), in a resteasy class, then returned to the other server, GSON will unmarshall to a listwrapper object, but the list inside is null. Any ideas?
Note: The list must remain generic because different objects may go into the list, though the list will always only have one type in it at a time.
Json
unmarshalling
GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create();
Object List;
if (!JSON.equals("")) {
List = gson.fromJson(new BufferedReader(new StringReader(JSON)), ListWrapper.class);
}
Listwrapper type
#XmlRootElement(name = "ListWrapper")
public class ListWrapper {
private Vector<Object> objects;
private String status;
private int batch;
private ValidationException e;
public ListWrapper() {
this.setStatus("Success");
}
public ListWrapper(Vector<Object> list) {
this.setStatus("Success");
this.objects = list;
}
public ListWrapper(int x) {
this.setStatus("batch");
this.batch = x;
}
public Vector<Object> getList() {
return objects;
}
public void setList(Vector<Object> object) {
this.objects = object;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
#XmlJavaTypeAdapter(ThrowableAdapter.class)
public ValidationException getE() {
if (e != null) {
return e;
} else {
return null;
}
}
public void setE(ValidationException x) {
this.e = x;
}
public int getBatch() {
return batch;
}
public void setBatch(int batch) {
this.batch = batch;
}
}
You cannot deserialize using directly ListWrapper, you need a container class, this is why you list is empty. By the way, your list is not a list but a map instead (curly braces limit content of list).
I created a code that correctly parse your JSON and provides you with some simple functionality to extract data. Since you want to keep flexible the data you are passing between the servers, you will need strings to access to data.
Here is the code ready to copy and paste to try it by yourself. Keep in mind that accessor methods are based on structure you are showing in the example. I provided you in main 4 different kind of data you can extract from it. Let me know if you need more information about that.
package stackoverflow.questions.q19817221;
import java.util.*;
import com.google.gson.Gson;
public class Q19817221 {
public class ListWrapper {
private Map list;
private Map status;
private Map batch;
private Object extractValue(Map m) {
return m.get("$");
}
public Integer getBatch() {
return Integer.valueOf( (String) extractValue(batch));
}
public Object getValueFromList(String key) {
try {
Map m = (Map) list.get(key);
if (m != null)
return extractValue(m);
} catch (Exception e) {
return list.get(key);
}
return null;
}
public Object getValueFromList(String secondLevelKey, String key) {
Map secondLevelMap = (Map) list.get(secondLevelKey);
try {
Map m = (Map) secondLevelMap.get(key);
if (m != null)
return extractValue(m);
} catch (Exception e) {
return list.get(key);
}
return null;
}
}
public class Container {
public ListWrapper ListWrapper;
}
/**
* #param args
*/
public static void main(String[] args) {
String json = "{\"ListWrapper\":{\"batch\":{\"$\":\"0\"},\"list\":{\"#xmlns\":{\"xsi\":\"http://www.w3.org/2001/XMLSchema-instance\"},\"#xsi:type\":\"fidsUsers\",\"createdBy\":{\"$\":\"administrator\"},\"createdWhen\":{\"$\":\"2013-02-25T17:29:19-05:00\"},\"endDate\":{\"$\":\"2016-10-28T00:00:00-04:00\"}," +
"\"isDisabled\":{\"$\":\"N\"},\"previousPasswords\":{\"$\":\"HXQDa4WxTdBmZtvhMVTgnw==####zW6bdHkKdMN2p6CgRNjNHA==####Sim7JN3kaHoXnh3KUS2++Q==####Emz7zU0Wrm0lyb/K522O5A==##ZirxzRl28JqfjOzIaMzAog==\"}," +
"\"primaryKey\":{\"$\":\"David\"},\"pswdChgDate\":{\"$\":\"2013-07-12T08:27:46-04:00\"},\"pswdCount\":{\"$\":\"0\"},\"roleId\":{\"$\":\"Admin\"},\"roleIdFidsRoles\":{\"globalAccess\":{\"$\":\"Y\"},\"primaryKey\":{\"$\":\"Admin\"},\"roleDesc\":{\"$\":\"Administrator\"},\"roleId\":{\"$\":\"Admin\"}," +
"\"updatedBy\":{\"$\":\"David\"}},\"startDate\":{\"$\":\"1992-07-28T00:00:00-04:00\"},\"updatedBy\":{\"$\":\"David\"},\"updatedWhen\":{\"$\":\"2013-10-02T10:46:31-04:00\"},\"userId\":{\"$\":\"David\"},\"userName\":{\"$\":\"David3\"},\"userPassword\":{\"$\":\"HXQDa4WxTdBmZtvhMVTgnw==\"}},\"status\":{\"$\":\"Success\"}}}";
Container c = new Gson().fromJson(json, Container.class);
ListWrapper lw = c.ListWrapper;
System.out.println("batch:" + lw.getBatch());
System.out.println("createdBy:" + lw.getValueFromList("createdBy"));
System.out.println("#xsi:type: " + lw.getValueFromList("#xsi:type"));
System.out.println("roleIdFidsRoles\\primaryKey: " + lw.getValueFromList("roleIdFidsRoles", "primaryKey"));
}
}
This is execution result:
batch:0
createdBy:administrator
#xsi:type: fidsUsers
roleIdFidsRoles\primaryKey: Admin
By looking into your ListWrapper class and a json you have posted - you have a mismatch and that is probably why it failed to unmarshall.
For:
{"ListWrapper":{"batch":{"$":"0"},"list":{"#xmlns":{"xsi":"http:\/\/www.w3.org\/2001\/XMLSchema-instance"},"#xsi:type":"fidsUsers","createdBy":{"$":"administrator"},"createdWhen":{"$":"2013-02-25T17:29:19-05:00"},"endDate":{"$":"2016-10-28T00:00:00-04:00"},"isDisabled":{"$":"N"},"previousPasswords":{"$":"HXQDa4WxTdBmZtvhMVTgnw==####zW6bdHkKdMN2p6CgRNjNHA==####Sim7JN3kaHoXnh3KUS2++Q==####Emz7zU0Wrm0lyb\/K522O5A==##ZirxzRl28JqfjOzIaMzAog=="},"primaryKey":{"$":"David"},"pswdChgDate":{"$":"2013-07-12T08:27:46-04:00"},"pswdCount":{"$":"0"},"roleId":{"$":"Admin"},"roleIdFidsRoles":{"globalAccess":{"$":"Y"},"primaryKey":{"$":"Admin"},"roleDesc":{"$":"Administrator"},"roleId":{"$":"Admin"},"updatedBy":{"$":"David"}},"startDate":{"$":"1992-07-28T00:00:00-04:00"},"updatedBy":{"$":"David"},"updatedWhen":{"$":"2013-10-02T10:46:31-04:00"},"userId":{"$":"David"},"userName":{"$":"David3"},"userPassword":{"$":"HXQDa4WxTdBmZtvhMVTgnw=="}},"status":{"$":"Success"}}} to me it is a object ListWrapper that has 3 fields named: batch, list and status, where batch is an object with one field being a number, list is a map and status is an object having one string in it.
I am not familiar with badgerfish, having said that, in your ListWrapper try to change private Vector<Object> objects to private Map<String,Object> objects (and of course matching getters/setters) and see if that will work
The list must remain generic because different objects may go into
the list, though the list will always only have one type in it at a
time.
If you know what type is it before un-marshalling, then ONLY it can work through the use of TypeToken
Sample code :
Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
gson.toJson(foo, fooType);
gson.fromJson(json, fooType);
Also, the definition will change to something like this :
public class ListWrapper {
private Vector<T> objects;
private String status;
private int batch;
}
or
public class ListWrapper {
private Vector<T extends someBaseObject> objects;
private String status;
private int batch;
}
depends on how exactly you decide to implement.

Categories