Attention! You may not strain the decision, just tell me: which direction I should think!
So, we have a very simple service:
#GET
#Path("/search")
#Consumes(MediaType.APPLICATION_JSON)
public Response getSubscriber(#QueryParam("data") SubscriberSearchFormData data){
System.out.println(data);
List <SubscrEntity> results = null //list of results
return Response.ok(results).build();
}
Used class SubscriberSearchFormData:
public class SubscriberSearchFormData {
private String name;
private String street;
private Integer contractNumber;
public static SubscriberSearchFormData fromString(String jsonRepresentation) throws Exception {
System.out.println("WE ARE HERE");
ObjectMapper mapper = new ObjectMapper(); // Jackson's JSON marshaller
SubscriberSearchFormData obj = null;
try {
obj = mapper.readValue(decoded, SubscriberSearchFormData.class);
} catch (IOException e) {
throw new Exception("Wrong JSON parameters!");
}
return obj;
}
//all getters and setters
}
On the idea, JSON should be automatically parsed by the method fromString() to the object of the SubscriberSearchFormData class. And we'll continue to work with him. But when I invoke the service:
localhost:8080/application/rest/catalog/subscriber/search?data={
"name":"bbb",
"street":"eee",
"contractNumber":5
}
Everything falls due to an error:
11:24:04,050 WARN [org.jboss.resteasy.resteasy_jaxrs.i18n] (default task-3) RESTEASY002130:
Failed to parse request.: javax.ws.rs.core.UriBuilderException: RESTEASY003330:
Failed to create URI: http://localhost:8080/application/rest/catalog/subscriber/search?data={%20%22name%22:%22bbb%22,%20%22street%22:%22eee%22,%20%22contractNumber%22:%225%22}
And at the same time, System.out.println ("WE ARE HERE"); is not even invoked. And it collapses, even before calling fromString ();
I dig for it the second day and can't solve.
It looks like your URI cannot be parsed correctly because it contains illegal characters; this issue was addressed here:
IllegalArgumentException caught when parsing URL with JSON String
this could also be useful: http://docs.jboss.org/resteasy/docs/3.0.7.Final/userguide/html_single/index.html#_QueryParam
Your issue could also be linked to: https://issues.jboss.org/browse/RESTEASY-1718
Related
I am trying to post a form to a Restlet ServerResource and read it into an object using Gson Restlet Extension.
There's no documentation on how to use it and nothing on StackOverflow.
What is the correct way of using gson restlet extension?
Following is what I have tried so far:
public class CustomerSegment {
private int visitsMin;
private int visitsMax;
// Getters, Setters and constructors
}
public class CampaignsResource extends ServerResource {
#Post
public Representation createCampaign(Representation entity) {
Form form = new Form(entity);
// Using form is the usual way, which works fine
// form: [[visitsMin=3], [visitsMax=6]]
CustomerSegment segment = null;
// Following hasn't worked
GsonConverter converter = new GsonConverter();
try {
segment = converter.toObject(entity, CustomerSegment.class, this);
//segment = null
} catch (IOException e1) {
e1.printStackTrace();
}
GsonRepresentation<CustomerSegment> gson
= new GsonRepresentation<CustomerSegment>(entity, CustomerSegment.class);
try {
segment = gson.getObject();
//NullPointerException
} catch (IOException e) {
e.printStackTrace();
}
return new EmptyRepresentation();
}
}
Form data that is being posted:
In fact, you can leverage the built-in converter support of Restlet without explicitly use the gson converter.
In fact, when you put the GSON extension within the classpath, the converter it contains is automatically registered within the Restlet engine itself. To check that you can simply use these lines when starting your application:
List<ConverterHelper> converters
= Engine.getInstance().getRegisteredConverters();
for (ConverterHelper converterHelper : converters) {
System.out.println("- " + converterHelper);
}
/* This will print this in your case:
- org.restlet.ext.gson.GsonConverter#2085ce5a
- org.restlet.engine.converter.DefaultConverter#30ae8764
- org.restlet.engine.converter.StatusInfoHtmlConverter#123acf34
*/
Then you can rely on beans within signatures of methods in your server resources instead of class Representation, as described below:
public class MyServerResource extends ServerResource {
#Post
public SomeOutputBean handleBean(SomeInputBean input) {
(...)
SomeOutputBean bean = new SomeOutputBean();
bean.setId(10);
bean.setName("some name");
return bean;
}
}
This works in both sides:
Deserialization of the request content into a bean that is provided as parameter of the handling method in the server resource.
Serialization into the response content of the returned bean.
You don't have anything more to do here.
For the client side, you can leverage the same mechanism. It's based on the annotated interfaces. For this, you need to create an interface defining what can be called on the resource. For our previous sample, it would be something like that:
public interface MyResource {
#Post
SomeOutputBean handleBean(SomeInputBean input);
}
Then you can use it with a client resource, as described below:
String url = "http://localhost:8182/test";
ClientResource cr = new ClientResource(url);
MyResource resource = cr.wrap(MyResource.class);
SomeInputBean input = new SomeInputBean();
SomeOutputBean output = resource.handleBean(input);
So in your case, I would refactor your code as described below:
public class CampaignsResource extends ServerResource {
private String getUri() {
Reference resourceRef = getRequest().getResourceRef();
return resourceRef.toString();
}
#Post
public void createCampaign(CustomerSegment segment) {
// Handle segment
(...)
// You can return something if the client expects
// to have something returned
// For creation on POST method, returning a 204 status
// code with a Location header is enough...
getResponse().setLocationRef(getUri() + addedSegmentId);
}
}
You can leverage for example the content type application/json to send data as JSON:
{
visitsMin: 2,
visitsMax: 11
}
If you want to use Gson, you should use this content type instead of the urlencoded one since the tool targets JSON conversion:
Gson is a Java library that can be used to convert Java Objects into
their JSON representation. It can also be used to convert a JSON string
to an equivalent Java object. Gson can work with arbitrary Java objects
including pre-existing objects that you do not have source-code of.
Hope it helps you,
Thierry
I'm calling this method and getting a 500 back from it.
In the debugger I'm able to step though it all the way to the return statement at the end. No problem, r is populated as expected after Response.build() is called, the status says 200 OK. But that's not what ends up getting produced. I've even told eclipse to break on any Exception.
#GET
#Path("/getAllAppMessagesAsXML")
#Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
public Response getAllAppMessagesXML(#QueryParam("applicationId") String applicationId){
ResponseList list = new ResponseList();
ArrayList<XmlMessageBean> xmlmessages = new ArrayList<>();
try {
List<AppMessage> messages = //Gets from a database
for(AppMessage m : messages){
XmlMessageBean xm = new XmlMessageBean();
xm.setId(m.getId());
xm.setApplicationId(m.getApplicationId());
xm.setMessageBody(m.getMessageBody());
xm.setMessageLevel(m.getMessageLevel());
xm.setMessageTitle(m.getMessageTitle());
xmlmessages.add(xm);
}
} catch (Exception e) {
logger.error("ERROR Failed to save Message AppMessageService.saveAppMessage()", e);
Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
list.setList(xmlmessages);
Response r = null;
try{
r = Response.status(Response.Status.OK).entity(list).build();
}catch(Exception e){
e.printStackTrace();
}
return r;
}
XmlMessageBean.java
#XmlRootElement(name="AppMessage")
#XmlAccessorType(XmlAccessType.FIELD)
public class XmlMessageBean {
#XmlElement
private Long id;
#XmlElement
private String applicationId;
#XmlElement
private String messageTitle;
#XmlElement
private String messageBody;
#XmlElement
private String messageLevel;
public XmlMessageBean(){
}
//getters and setters
}
ResponseList.java
#XmlRootElement(name = "ResponseList")
public class ResponseList {
public ResponseList(){
}
#XmlElement(name="list")
private List<XmlMessageBean> list;
public List<XmlMessageBean> getList() {
return list;
}
public void setList(List<XmlMessageBean> list) {
this.list = list;
}
}
I've got this all running in a jersey.servlet.ServletContainer
I'm stumped. I can't figure out how to get it to produce any kind of error message other than a generic 500. I've tried setting up an exception mapper as some other posts have mentioned but this also isn't picking anything up.
IllegalAnnotationException: Class has two properties of the same name "list"
Look at your two model classes XmlMessageBean and ResponseList. Do you see any difference? The main difference (and the cause for the error), is the #XmlAccessorType(XmlAccessType.FIELD) annotation (or lack there of). JAXB by default will look for the public properties (JavaBean getters/setters). So that's one property. But then you define another property by using the #XmlElement annotation on the field. The reason it works for XmlMessageBean is that it overrides the default public property lookup by changing it to XmlAccessType.FIELD
So you can simply annotate the ResponseList with #XmlAccessorType(XmlAccessType.FIELD) and it should work. You could also simply get rid of all the #XmlElement annotations, and get rid of #XmlAccessorType(XmlAccessType.FIELD), and it will still work, as it will look up the JavaBean properties. Generally, for me I only use the #XmlElement annotations when I need to change the name of the property, and just put it on the getter, for example.
private String messageBody;
#XmlElement(name = "body")
public String getMessageBody(){
return messageBody;l
}
Other than that, I normally leave out the annotation, and also the #XmlAccessorType annotation, and just let it resolve to the default behavior.
Might be a strange question but indeed I would like to achieve a a bit more coverage on my tests and although I coded against a JsonProcessingException I can't create a payload that generates this exception, maybe because Jackson is quite smart and converts everything to a string, and even for bad strings it goes around the JSON specs. My problem is that Jackson is quite good :)
I basically want a payload that when I run this, it break with JsonProcessingException:
String jsonPayload = objectMapper.writeValueAsString(payload);
I've tried some like:
HashMap<String, String> invalidJSONPayload= new HashMap<>();
invalidJSONPayload.put("021",021);
invalidJSONPayload.put("---",021);
invalidJSONPayload.put("~",021);
I'm not fussed with the type, so feel free to suggest another one. An empty object for example, throws JsonMappingException and I already catch that one as well.
I wanted to do the same thing, and eventually accomplished it by using the Mockito "spy" function, which wraps a real object with a mock object. All calls to the mock object get forwarded to the real object, except those you are trying to mock. For example:
ObjectMapper om = Mockito.spy(new ObjectMapper());
Mockito.when( om.writeValueAsString(ErrorObject.class)).thenThrow(new JsonProcessingException("") {});
All usages of om will be handled by the underlying ObjectMapper instance until an instance of ErrorObject gets passed in, at which point the JsonProcessingException will be thrown.
The newJsonProcessingException is created as an anonymous class, as it is a protected class and only a sub-class can be instantiated.
Building off of Liam's answer, mocking the toString() method with a cycle also causes Jackson to break.
#Test
public void forceJsonParseException() {
try {
Object mockItem = mock(Object.class);
when(mockItem.toString()).thenReturn(mockItem.getClass().getName());
new ObjectMapper().writeValueAsString(mockItem);
fail("did not throw JsonProcessingException");
} catch (JsonProcessingException e) {
//pass
}
}
EDIT: It's way easier than that. A Mockito mock will always throw it. o.o;;
You could use something like this:
private static class ClassThatJacksonCannotSerialize {
private final ClassThatJacksonCannotSerialize self = this;
#Override
public String toString() {
return self.getClass().getName();
}
}
Which results in a JsonProcessingException with message Direct self-reference leading to cycle (through reference chain: ClassThatJacksonCannotSerialize["self"])
following on #Mike.Mathieson answer
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
public class JacksonTest {
#Test(expected = JsonProcessingException.class)
// actually throws an InvalidDefinitionException (which extends JsonProcessingException)
public void encodeThrowsException() throws JsonProcessingException {
new ObjectMapper().writeValueAsString(new Object());
}
}
https://fasterxml.github.io/jackson-databind/javadoc/2.9/com/fasterxml/jackson/databind/exc/InvalidDefinitionException.html
note that this test won't work if the ObjectMapper have been configured to disable SerializationFeature.FAIL_ON_EMPTY_BEANS, e.g.
new ObjectMapper()
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
.writeValueAsString(new Object());
For me, if a class has no public fields/methods, writeValueAsString will throw a JsonMappingException (no serializer found for class...)
private static class ClassThatJacksonCannotSerialize {}
private void forceProcessingException() {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(value);
}
catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
Throw exception in getter to simulate JsonProcessingException.
public static class TestData {
public String getEx() throws JsonProcessingException { throw new JsonParseException(null, "test"); }
}
ObjectMapper().writeValueAsString(new TestData());
You can get a JsonProcessingException if mapping two fields to the same property.
class InvalidObject {
#JsonProperty("s")
private String x = "value1";
#JsonProperty("s")
private String y = "value2";
}
Exception message is "Multiple fields representing property "s":..."
Trying to mock using mock(ObjectMapper.class) will invariably result in Checked exception is invalid for this method! as it is not possible to throw checked exception (JsonProcessingException extends IOException). Creating a self referencing value object like other answers suggested could be too convoluted for many cases and looks like a hack.
The easiest way I found is to extend ObjectMapper and then use that in your test method. You should pass the subclass to SUT
#Test
public void buildJsonSwallowsJsonProcessingException() {
class MyObjectMapper extends ObjectMapper {
#Override
public String writeValueAsString(Object value)
throws com.fasterxml.jackson.core.JsonProcessingException {
throw new com.fasterxml.jackson.core.JsonProcessingException("Forced error") {};
}
}
ObjectMapper objectMapper = new MyObjectMapper();
SUTBean sutbean = new SUTBean(objectMapper);
sutbean.testMethod();
assertTrue(expected, actual);
}
i was land on this question because i had the same target of the questioner:
Might be a strange question but indeed I would like to achieve a a bit
more coverage on my tests
IMHO Mockito solution it's more elegant and not introduce misunderstanding but I challenged a colleague to find another solution and he did. This is the solution:
#Test
#DisplayName("Object To Json KO")
void objectToJsonKOTest() {
KafkaMessageDTO o = new KafkaMessageDTO() {
#Override
public String getAuthCode() {
int a = 2/0;
return super.getAuthCode();
}
};
String s = mapper.writeValueAsString(o);
Assertions.assertTrue(s.isEmpty());
}
This is trash, high level trash :) , but it works and I wanted to share it with you as an alternative to mock
ByeBye
I found this in Jackson Github issue; but it solved my problem and I am able to throw the JsonProcessingException.
#Test
public void forceJsonParseException() {
try {
Object mockItem = mock(Object.class);
when(mockItem.toString()).thenReturn(mockItem.getClass().getName());
new ObjectMapper().writeValueAsString(mockItem);
fail("did not throw JsonProcessingException");
} catch (JsonProcessingException e) {
//pass
}
}
Now how I used this in my code
Need to test this method
public String geResponse(MyObject myObject) {
try {
return objectMapper.writeValueAsString(myObject);
} catch (JsonProcessingException e) {
log.error("Service response JsonParsing error {} ", e.getMessage());
return "Validation Service response JsonParsing error {} "+ e.getMessage();
}
}
This how I test the JsonProcessingException
#SneakyThrows
#Test
public void testGetValidationResponseNegative() {
MyObject mockItem = mock(MyObject.class);
when(mockItem.toString()).thenReturn(mockItem.getClass().getName());
String vr = geResponse(mockItem);
assertTrue(!vr.isEmpty());
}
I hope this helps!!!
You can get JsonProcessingException via below code
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class SomePOJOClass
{
// Lets assume there are your fields here.
}
// ...
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode objectNode = new ObjectNode();
objectNode.put("unknown-field-in-the-class", "and-its-value");
SomePOJOClass somePOJOClass = objectMapper.treeToValue(objectNode, SomePOJOClass .class);
Use this value, it shall break, it broke for me::
ObjectMapper objectMapper = new ObjectMapper();
String jsonStringValue ="{'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}";
try {
MyCustomClass classObject = objectMapper.readValue(jsonStringValue, MyCustomClass.class);
} catch(JsonProcessingException ex) {
//LOGGER / SYSOUT here
}
ERROR::JsonProcessingException / JsonParseException
Unexpected character (''' (code 39)): was expecting double-quote to start field name
It really worked for me.. cheers..
I'm using Gson to parse responses from a server on Android. Each response has some useless (to me) data on it that complicates my Gson models. Here is the general hierarchy of json returned:
response: {
date: 1406253006807,
otherUselessData1: "This is some useless data",
otherUselessData2: "This is some useless data",
usefulJsonObject: { <---- This is really the object that I care about
}
}
Everything above or at the same level as usefulJsonObject I could really do without. The useless data is returned for every request, and the actual response is embedded beneath as the usefulJsonObject. This wouldn't be a big problem but it's really cluttering up my gson model objects.
For example:
Let's say I have 3 requests I can make: A, B, and C. For each response it seems I need to make a minimum of 3 custom classes.
public class ResponseA {
#SerializedName("response") ResponseObjectA responseObject;
public static class ResponseObjectA {
#SerializedName("usefulJsonObject") UsefulObjectA usefulObject;
}
public static class UsefulObjectA {
}
}
I've tried a few solutions, but I haven't found anything elegant that wouldn't add an extra step to my process. I'm using retrofit to do my http requests and it's really nice that it just returns the fully parsed gson object to me. I've thought of other solutions like having the useful object just be a JsonElement and then doing a 2nd gson call after the first comes back. Again, not ideal.
I just wanted to know if I was missing something. Surely I'm not the only one who's encountered something like this, and so I thought I'd ask how other people would handle something like this.
It is initialization Instance value, not NULL value. Check my example.
Address.java
public class Address {
public Address(){
}
}
Person.java
public class Person {
private String name;
private String nrc;
private Address address;
public Person(String name, String nrc, Address address) {
this.name = name;
this.nrc = nrc;
this.address = address;
}
}
The following Json string is equalvent to
Person person = new Person("Zaw Than Oo", "11111", null);
{
"name": "Zaw Than Oo",
"nrc": "11111"
}
The following Json string is equalvent to
Person person = new Person("Zaw Than Oo", "11111", new Address());
{
"name": "Zaw Than Oo",
"nrc": "11111",
"address": {} <-- here use less object for you.
}
Even if you don't create new Instance, Other lib/api(you used) may be create that instance by Reflection.
Short to the Point
{
...
"xxx": {} --> new instance without data/value
...
}
{
...
--> null value
...
}
I never found an elegant way dealing with just Gson. I tried several options with Generics, all of which didn't work or left something to be desired.
Since I'm using Retrofit, I decided to override the GsonConverter, and just filter out the unnecessary information from all my requests. It ends up not being as flexible, as in I can't use the same Retrofit network interface for calls to other servers, but I'm not really doing that, and it also has the down side of having 2 rounds of json parsing calls (meh). You could probably do this more efficiently, but this is working for me for now.
public class CustomGsonConverter extends GsonConverter {
private Gson mGson;
public CustomGsonConverter(Gson gson) {
super(gson);
this.mGson = gson;
}
public CustomGsonConverter(Gson gson, String encoding) {
super(gson, encoding);
this.mGson = gson;
}
#Override public Object fromBody(TypedInput body, Type type) throws ConversionException {
try {
CustomResponse customResponse = mGson.fromJson(new InputStreamReader(body.in()), CustomResponse.class);
return mGson.fromJson(customResponse.responseObject.data, type);
} catch (IOException e) {
throw new ConversionException(e);
}
}
public static class CustomResponse {
#SerializedName("rsp") ResponseObject responseObject;
public static class ResponseObject {
// #SerializedName("date") long date;
#SerializedName("data") JsonElement data;
}
}
}
Maybe there is a better way that I'm just not realizing.
Now a days I'm having experience of Json parsing .Not have much practice with collection.I have a Json String
{
"time":1352113682,
"api_version":"1",
"firstname":"abc",
"lastname":"xyz",
"company":"Cool Apps",
"email":"abc#apps.com.au"
}
I made class
public class AuthenticateUser implements Serializable{
// Response when Successfully Login
public String time;
public String api_version;
public String firstname;
public String lastname;
public String company;
public String email;
}
And trying to parse it like this
Map<String, AuthenticateUser> map=null;
ObjectMapper mapper=new ObjectMapper();
try{
map=mapper.readValue(result,new TypeReference<Map<String, AuthenticateUser>>(){});
Set<String> keys=map.keySet();
for (String key : keys) {
System.out.println(map.get(key).time);
System.out.println(map.get(key).api_version);
System.out.println(map.get(key).firstname);
System.out.println(map.get(key).lastname);
System.out.println(map.get(key).company);
System.out.println(map.get(key).email);
}
}catch (Exception e) {
e.printStackTrace();
}
But gettin this error
com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [simple type, class AuthenticateUser] from JSON integral number; no single-int-arg constructor/factory method
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromInt(StdValueInstantiator.java:316)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromNumber(BeanDeserializer.java:427)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:119)
at com.fasterxml.jackson.databind.deser.std.MapDeserializer._readAndBindStringMap(MapDeserializer.java:429)
at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:310)
at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:26)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2577)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1817)
at Driver$1.run(Driver.java:140)
The example you gave at the beginning is for a single AuthenticateUser object. Is that all that is being passed to this program as input? This absolutely will not parse properly.
A single AuthenticateUser is not a Map of type (String, AuthenticateUser)
I think maybe you're confused, why are you making a map? If you intended to parse only a single object, all you need is:
AuthenticateUser user =mapper.readValue(result, AuthenticateUser.class);
System.out.println(user.time);
How does that look? Are you passing a map in JSON?