Deserializing List of Objects with Mirah for Codename One - json

I am trying to use mirah for JSON to POJO mapping in an codenameone application. It works finde when i want to map a Simple JSON like
{"id":"1","name":"foo","classification":"10"}
With this class:
public class Brand {
private String id;
private String name;
private String classification;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassification() {
return classification;
}
public void setClassification(String classification) {
this.classification = classification;
}
}
Now I want to wrap it in a Message Object, where i have a list of brands:
import java.util.List;
public class Message {
public List<Brand> brands;
public List<Brand> getBrands() {
return brands;
}
public void setBrands(List<Brand> brands) {
this.brands = brands;
}
}
I use this Mirah Script for mapping:
data_mapper Message:MessageMapper
data_mapper Brand:BrandMapper
like shannah described here.
My code where I call my Webservice:
MessageMapper scheduleMapper = new MessageMapper();
DataMapper.createContext(Arrays.asList(
scheduleMapper,
new BrandMapper()
), new DataMapper.Decorator() {
public void decorate(DataMapper mapper) {
mapper.setReadKeyConversions(Arrays.asList(DataMapper.CONVERSION_NONE));
}
});
try {
Message message = scheduleMapper.readJSONFromURL("http://localhost/php-REST-DigitaleMusterplatte/api.php/brands", Message.class);
System.out.println(message);
} catch (IOException ex) {
Log.e(ex);
}
This is the json response:
{"brands":[{"id":"1","name":"foo","classification":"10"},{"id":"2","name":"bar","classification":"20"}]}
I get this exception:
java.lang.RuntimeException: Failed to get key brands for class interface java.util.List because it was not a registered object type
at ca.weblite.codename1.mapper.DataMapper.get(DataMapper.java:507)
at com.mycompany.app.dmp.models.MessageMapper.readMap(/Volumes/Windows VMS/Documents/Shared/NetBeansProjects/mirah_macro_utils/MirahMacroUtils/src/ca/weblite/mirah/utils/DataMapperBuilder.mirah)
at ca.weblite.codename1.mapper.DataMapper.readMap(DataMapper.java:719)
at ca.weblite.codename1.mapper.DataMapper.readJSON(DataMapper.java:780)
at ca.weblite.codename1.mapper.DataMapper.readJSON(DataMapper.java:792)
at ca.weblite.codename1.mapper.DataMapper.readJSONFromConnection(DataMapper.java:767)
at ca.weblite.codename1.mapper.DataMapper.readJSONFromURL(DataMapper.java:754)
at com.mycompany.myapp.MyApplication.start(MyApplication.java:96)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.codename1.impl.javase.Executor$1$1.run(Executor.java:123)
at com.codename1.ui.Display.processSerialCalls(Display.java:1152)
at com.codename1.ui.Display.mainEDTLoop(Display.java:969)
at com.codename1.ui.RunnableWrapper.run(RunnableWrapper.java:120)
at com.codename1.impl.CodenameOneThread.run(CodenameOneThread.java:176)
The demo application OSCONScheduler works fine.

This looks like a bug. But try changing brands to be private instead of public. It could be getting confused over whether to use your accessor/mutable or to use the public var.

Related

GluonConnect JSON converter cannot convert object

On the client app I have this POJO
public class Chicken {
private String name;
private int age;
public Chicken(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
and I run this
RestClient get = RestClient.create().method("GET")
.host("http://localhost:8080/DevCrowd")
.path("resources/chickens");
GluonObservableList<Chicken> sample = DataProvider.retrieveList(
get.createListDataReader(Chicken.class));
System.out.println(sample);
But I get the error:
WARNING: Failed to create object of type class com.devcrowd.test.Chicken from the following json object {"id":1,"name":"AAA","age":12}
java.lang.InstantiationException: com.gluonhq.notesapp.Chicken
at java.lang.Class.newInstance(Class.java:427)
at com.gluonhq.connect.converter.JsonConverter.readFromJson(JsonConverter.java:111)
at com.gluonhq.connect.converter.JsonIterableInputConverter.next(JsonIterableInputConverter.java:108)
at com.gluonhq.connect.provider.DataProvider.lambda$retrieveList$21(DataProvider.java:194)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NoSuchMethodException: com.gluonhq.notesapp.Chicken.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
... 6 more
On the server I have this entity:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement
#Entity
#NamedQuery(name="all", query = "SELECT c FROM Chicken C")
public class Chicken {
#Id
#GeneratedValue
private long id;
private String name;
private int age;
public Chicken() {}
public Chicken(String name, int age) {
this.name = name;
this.age = age;
}
}
and this service:
#Path("chickens")
public class ChickensResource {
#Inject
ChickenService cs;
#GET
#Produces(MediaType.APPLICATION_JSON)
public String chickens() {
JsonArrayBuilder jsonArrayBuilder = Json.createArrayBuilder();
List<Chicken> chickens = cs.getAllChickens();
chickens.stream().map(chicken -> Json.createObjectBuilder()
.add("name", chicken.getName())
.add("age", chicken.getAge())
.build())
.forEach(jsonArrayBuilder::add);
return jsonArrayBuilder.build().toString();
}
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public void save(JsonObject chicken) {
String name = chicken.getString("name");
int age = chicken.getInt("age");
cs.save(new Chicken(name, age));
}
}
I can POST correctly (I check the DB and what I POST is there so this is why the error stack has a Chicken object ready) but I can't read it back. Why is that?
As you can read in the docs for JsonConverter::readFromJson:
Convert the provided JSON Object into a Java object. If a new instance could not be created from the specified targetClass in the constructor, then null will be returned.
The conversion works by inspecting all the property methods of the target class. A property method is any field that has both a getter and a setter method.
Now if you check your exception:
java.lang.InstantiationException: com.gluonhq.notesapp.Chicken
at java.lang.Class.newInstance(Class.java:427)
the reason becomes clear: the target class (com.gluonhq.notesapp.Chicken) can't be instantiated, because it looks for a parameterless constructor.
All you'll need to do is add one:
public class Chicken {
private String name;
private int age;
public Chicken() { }
public Chicken(String name, int age) {
this.name = name;
this.age = age;
}
...
}
EDIT
The DataProvider returns an observable list, and you should use the initializedProperty() to find out when the list is ready, so you can get its content:
RestClient get = RestClient.create().method("GET")
.host("http://localhost:8080/DevCrowd")
.path("/resources/chickens");
GluonObservableList<Chicken> sample = DataProvider.retrieveList(
get.createListDataReader(Chicken.class));
sample.initializedProperty().addListener((obs, ov, nv) -> {
if (nv) {
for (Chicken chicken : sample) {
System.out.println(chicken);
}
}
});

Unable to get through CXF with JSON

I am trying to build a CXF RESTFul service with JSON as input and output. I am using JAXRSServerFactoryBean to boot my service. When I try to hit the URL from a client program, I am getting the following exception. My program is very simple and attached the same at the bottom.
Please help.
May 19, 2015 11:03:30 PM org.apache.cxf.jaxrs.provider.AbstractJAXBProvider handleExceptionStart
WARNING: javax.xml.stream.XMLStreamException: ParseError at [row,col]:[0,0]
Message: A JSONObject text must begin with '{' at character 0 of
at org.codehaus.jettison.mapped.MappedXMLInputFactory.createXMLStreamReader(MappedXMLInputFactory.java:51)
at org.codehaus.jettison.AbstractXMLInputFactory.createXMLStreamReader(AbstractXMLInputFactory.java:116)
at org.apache.cxf.jaxrs.provider.json.utils.JSONUtils.createStreamReader(JSONUtils.java:162)
at org.apache.cxf.jaxrs.provider.json.JSONProvider.createReader(JSONProvider.java:290)
at org.apache.cxf.jaxrs.provider.json.JSONProvider.createReader(JSONProvider.java:280)
at org.apache.cxf.jaxrs.provider.json.JSONProvider.readFrom(JSONProvider.java:233)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromMessageBodyReader(JAXRSUtils.java:1337)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromMessageBody(JAXRSUtils.java:1288)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameter(JAXRSUtils.java:824)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameters(JAXRSUtils.java:787)
at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.processRequest(JAXRSInInterceptor.java:212)
at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.handleMessage(JAXRSInInterceptor.java:77)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:307)
May 19, 2015 11:03:30 PM org.apache.cxf.jaxrs.impl.WebApplicationExceptionMapper toResponse
WARNING: javax.ws.rs.BadRequestException: HTTP 400 Bad Request
at org.apache.cxf.jaxrs.utils.SpecExceptions.toBadRequestException(SpecExceptions.java:84)
at org.apache.cxf.jaxrs.utils.ExceptionUtils.toBadRequestException(ExceptionUtils.java:114)
at org.apache.cxf.jaxrs.provider.AbstractJAXBProvider.handleExceptionEnd(AbstractJAXBProvider.java:705)
at org.apache.cxf.jaxrs.provider.AbstractJAXBProvider.handleXMLStreamException(AbstractJAXBProvider.java:734)
at org.apache.cxf.jaxrs.provider.json.JSONProvider.readFrom(JSONProvider.java:261)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromMessageBodyReader(JAXRSUtils.java:1337)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromMessageBody(JAXRSUtils.java:1288)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameter(JAXRSUtils.java:824)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameters(JAXRSUtils.java:787)
at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.processRequest(JAXRSInInterceptor.java:212)
at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.handleMessage(JAXRSInInterceptor.java:77)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:307)
at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:251)
at org.apache.cxf.transport.http_jetty.JettyHTTPDestination.doService(JettyHTTPDestination.java:234)
at org.apache.cxf.transport.http_jetty.JettyHTTPHandler.handle(JettyHTTPHandler.java:70)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1129)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1065)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:215)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
Caused by: javax.xml.stream.XMLStreamException: ParseError at [row,col]:[0,0]
Message: A JSONObject text must begin with '{' at character 0 of
at org.codehaus.jettison.mapped.MappedXMLInputFactory.createXMLStreamReader(MappedXMLInputFactory.java:51)
at org.codehaus.jettison.AbstractXMLInputFactory.createXMLStreamReader(AbstractXMLInputFactory.java:116)
at org.apache.cxf.jaxrs.provider.json.utils.JSONUtils.createStreamReader(JSONUtils.java:162)
at org.apache.cxf.jaxrs.provider.json.JSONProvider.createReader(JSONProvider.java:290)
RestFulServiceStarter
public class RestFulServiceStarter {
public static void main(String[] args) {
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
sf.setResourceClasses(ProfileService.class);
sf.setResourceProvider(ProfileService.class,
new SingletonResourceProvider(new ProfileServiceImpl()));
sf.setAddress("http://localhost:9999/");
Server server = sf.create();
}
}
ProfileService
#Path("/profile/")
public interface ProfileService {
#GET
#Path("/static/")
#Consumes({ MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_JSON })
public Response getStaticProfiles(ProfileRequest pr);
}
ProfileServiceImpl
public class ProfileServiceImpl implements ProfileService {
public Response getStaticProfiles(ProfileRequest pr) {
return Response.status(200).entity(pr).build();
}
}
ProfileRequest
#XmlRootElement ( name = "profile" )
public class ProfileRequest {
private String name="";
private String country="";
private String region="";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
}
Your interface method is annotated with a #GET method, yet it also has an entity parameter (ProfileRequest). Try #POST.

Jersey - Serializing a POJO with json - nested objects will be ignored

HiAll,
i have the following problem in development branch:
If i use the jersey REST service to serialize the POJO containig a list of the nested other POJO, then this nested POJO will be NOT serialized. This problem is reproducible only in branch.
If i prototype using POJO structur liked in branch, i have no problem.
The Details:
The POJO (Domain):
public class Article {
private int id;
private int name;
// getter & setter
}
The POJO ( DTO)
public class DTO {
private List<Article> articles;
private String message;
public DTO {
articles= new ArrayList<>();
}
getArticles() {
return articles;
}
getArticlesCount() {
return articles.size();
}
public void getMessage() {
return message;
}
public String getMessage() {
return message;
}
....
}
It will be created the 1 article with id = 1 and name "firstArticle" the
The results of serialization after service call to find all articles:
in branch
*{"message":"1 article found","articlesCount":1
}*
in prototype
{
{"message":"1 article found","articlesCount":1[{"id":1,"name":"firstArticle"]}
}
i have no idia what's happened. I checked out all settings (web.xml, jersey version, etc.)
If you use Jackson JSON library, your POJO will be automatically handled by Jackson JSON lib to convert between JSON and POJO. You also need to add the following lines in your web.xml:
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
Then in your Jersey resource class, use #Produces and #Consumes annotation to indicate the data format is JSON:
#Path("/myResource")
#Produces("application/json")
public class SomeResource {
#GET
public String doGetAsJson() {
...
}
}
Follow the above answer to set up Jersey POJO mappping, and then try the following nested POJO example.
Article class:
public class Article {
private int id;
private String name;
public Article(int id, String name) {
this.id = id;
this.name = name;
}
//getter and setter ...
}
The Book class that contains the Article class:
public class Book {
private List<Article> articles;
private String message;
private int articleCount;
public List<Article> getArticles() {
return articles;
}
public void setArticles(List<Article> articles) {
this.articles = articles;
setArticleCount(articles.size());
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getArticleCount() {
return articleCount;
}
public void setArticleCount(int articleCount) {
this.articleCount = articleCount;
}
}
Finally in the Jersey resource class NestedPojoResource:
#Path("/nested-pojo")
public class NestedPojoResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Book getNestedPojo() {
Book book = new Book();
List<Article> articles = new ArrayList<Article>();
articles.add(new Article(1, "firstArticle"));
articles.add(new Article(2, "secondArticle"));
book.setArticles(articles);
book.setMessage(book.getArticleCount() + " articles found");
return book;
}
}
When you go to http://example.com/myappname/nested-pojo, you will see the correct JSON output which contains the nested POJO fields:
{"articles":[{"id":1,"name":"firstArticle"},{"id":2,"name":"secondArticle"}],"message":"2 articles found","articleCount":2}

Jersey unmarshal JSON: Last element null does not work

I am using Jersey to parse the following JSON:
{"response":{"status":"OK","campaigns":[{"id":12345,"state":"active","code":null}]}}
But I get the following error message:
java.lang.IllegalArgumentException: No more parsing elements.
If I switch the position of the fields code and state so that the resulting JSON looks like
{"response":{"status":"OK","campaigns":[{"id":12345,"code":null,"state":"active"}]}}
everything works fine. Also if I change the code-field in the first JSON to a non-null value like "code":"test", Jersey can parse this without any problems. I tried other more complex examples always getting the above mentioned error message when leaving the last field of any element of an array null.
I think I am doing something wrong, because I could not find any others having the similar problem. I already tried to implement a CustomJAXBContextResolver using other JSON notations like natural but nothing worked for me.
Any ideas?
Here are my binding classes:
#XmlRootElement
public class LoadEntityResponse {
public LoadEntityResponse() {
}
private Response response;
public Response getResponse() {
return response;
}
public void setResponse(Response response) {
this.response = response;
}
}
and
public class Response {
public Response() {
}
private String status;
private String error;
private String error_id;
private Campaign[] campaigns;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public String getError_id() {
return error_id;
}
public void setError_id(String error_id) {
this.error_id = error_id;
}
public Campaign[] getCampaigns() {
return campaigns;
}
public void setCampaigns(Campaign[] campaigns) {
this.campaigns = campaigns;
}
}
and finally
public class Campaign{
public Campaign() {
}
protected int id;
protected String code;
protected String state;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
Solved: Using JacksonJsonProvider now:
...
DefaultClientConfig config = new DefaultClientConfig();
config.getClasses().add(JacksonJsonProvider.class);
...
that´s all!
You can also use Jackson POJO support that comes with jersey-json but there is a need to do some configuration, see POJO support in Jersey User Guide.
Try using Genson http://code.google.com/p/genson/.
To enable it on client side use the following code:
ClientConfig config = new DefaultClientConfig();
config.getClasses().add(GensonJsonConverter.class);
cli = Client.create(config);
EDIT: on server side there is no configuration needed, when the jar is in your classpath json support is automatically enabled.

Why Jersey refuses to marshal generic types?

I have a base class generic with generic type property and several classes inheriting from it. Something like that:
public abstract class BaseClass<T extends Number> {
#XmlAnyElement
public T getId() { return id; }
private T id ; // init for hibernate bug workaround
.....
}
#XmlRootElement
public class A extends BaseClass<Integer> {
private String name;
private String error;
private String url;
public String getError() { return error; }
public void setError(String error) { this.error = error; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public void setUrl(String url) { this.url = url; }
public String getUrl() { return url; }
}
When Jersey tries to marshals it throws:
javax.xml.bind.MarshalException
- with linked exception:
[com.sun.istack.SAXException2: unable to marshal type "java.lang.Integer" as an element because it is missing an #XmlRootElement annotation]
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:318)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:172)
at com.sun.jersey.json.impl.JSONMarshallerImpl.marshal(JSONMarshallerImpl.java:94)
at com.sun.jersey.json.impl.BaseJSONMarshaller.marshallToJSON(BaseJSONMarshaller.java:103)
at com.sun.jersey.json.impl.BaseJSONMarshaller.marshallToJSON(BaseJSONMarshaller.java:91)
......
I can't add XmlRootElement annotation to Integer, so what I supposed to do?
You should try to add #XmlRootElement annotation to your BaseClass.