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..
Related
I receive different objects set from the API. Each response have a follow structure:
items:[
{
user_id:1,
tags: {..}
},
{..}
]
The problem is that I do not want so unuseful and not readable structure.
I mean, all my methods (I use Retrofit library) must have some next signature:
Call<UserRepresantation>...
Call<RepoRepresentation>...
instead
Call<List<Users>>
Call<List<Repos>>
And also I have to use additional entities every time:
class UserRepresentation{
List<Users> items;
}
The Retrofite has possibility to use different converters for the serialization, for example:
Retrofit.Builder()
.baseUrl(stckUrl)
.addConverterFactory(GsonConverterFactory.create(new Gson())) < --- converter applying
.build();
As I understand I can use JsonSeializer to configure such behavior, but I can't figure out in which way. Can anyone help me to solve this issue?
So, in the simple words:
we have a response:
items:[
{
user_id:1,
tags: {..}
},
{..}
]
And we need to receive:
List<Users> = gson.fromJson(respose, User.class);
One solution would be to write a TypeAdapterFactory which performs the unwrapping when asked to deserialize any List<User> and List<Repo>, or in general for any List. However, the problem with this is that it would also apply to any nested lists of these types, for example when your User class has a field List<Repo> repos then that adapter factory would also try to unwrap its value, and fail.
So a more reliable solution might be to implement a TypeAdapterFactory which keeps track of whether it is currently being used to deserialize the top-level value and in that case unwrap / flatten the data. If not used for the top-level value it could simply let the other registered adapter factories handle the data:
class FlatteningTypeAdapterFactory implements TypeAdapterFactory {
public static final FlatteningTypeAdapterFactory INSTANCE = new FlatteningTypeAdapterFactory();
private FlatteningTypeAdapterFactory() { }
/** Tracks whether this is a nested call to this factory */
private static final ThreadLocal<Boolean> isNestedCall = new ThreadLocal<>();
#Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
// Only handle top-level value, for nested calls let other factory handle it
// Uses Boolean.TRUE.equals to handle case where value is `null`
if (Boolean.TRUE.equals(isNestedCall.get())) {
return null;
}
TypeAdapter<T> delegate;
isNestedCall.set(true);
try {
delegate = gson.getDelegateAdapter(this, type);
} finally {
isNestedCall.remove();
}
return new TypeAdapter<T>() {
#Override
public void write(JsonWriter out, T value) {
throw new UnsupportedOperationException();
}
#Override
public T read(JsonReader in) throws IOException {
in.beginObject();
String name = in.nextName();
if (!name.equals("items")) {
throw new IllegalArgumentException("Unexpected member name: " + name);
}
T value;
// While using delegate adapter also set isNestedCall in case delegate looks up
// another adapter dynamically while its `read` method is called
isNestedCall.set(true);
try {
value = delegate.read(in);
} finally {
isNestedCall.remove();
}
in.endObject();
return value;
}
};
}
}
You would then have to register it with a GsonBuilder before constructing the GsonConverterFactory:
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(FlatteningTypeAdapterFactory.INSTANCE)
.create();
Note that the code above has not been extensively tested; there might be bugs or corner cases where it does not work correctly.
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
I am using a library com.fasterxml.jackson library for JsonSchema,
I am creating an IntegerSchema object, when I set range for integer schema using below code:
main(){
IntegerSchema intSchema = new IntegerSchema();
// setMaximum accepts Double object
intSchema.setMaximum(new Double(102000000));
// setMaximum accepts Double object
intSchema.setMinimum(new Double(100));
printJsonSchema(intSchema);
}
public void printJsonSchema(JsonSchema schema){
ObjectMapper mapper = new ObjectMapper();
try {
logger.info(mapper.writeValueAsString(schema));
} catch (JsonProcessingException e) {
throw new IllegalStateException(e);
}
}
When I convert IntegerSchema to string using ObjectMapper getting below response:
{"type":"integer","maximum":1.02E8,"minimum":100.0}
maximum and minimum values are getting converted to scientific notation.
But I need output in non scientific notation as below:
{"type":"integer","maximum":102000000,"minimum":100}
I cannot change IntegerSchema class.
Please suggest how to get the required output without extending IntegerSchema class?
Thanks in advance
this is a java issue somewhat I believe. If you debug your program, your Double will always be displayed scientifically, so what we'll want is to force it into a String. This can be achieved in Java in multiple ways, and you can look it up here:
How to print double value without scientific notation using Java?
In terms of your specific question about Jackson, I've written up some code for you:
public class ObjectMapperTest {
public static void main(String[] args) throws JsonProcessingException {
IntegerSchema schema = new IntegerSchema();
schema.type = "Int";
schema.max = 10200000000d;
schema.min = 100d;
ObjectMapper m = new ObjectMapper();
System.out.println(m.writeValueAsString(schema));
}
public static class IntegerSchema {
#JsonProperty
String type;
#JsonProperty
double min;
#JsonProperty
#JsonSerialize(using=MyDoubleDesirializer.class)
double max;
}
public static class MyDoubleDesirializer extends JsonSerializer<Double> {
#Override
public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers)
throws IOException, JsonProcessingException {
// TODO Auto-generated method stub
BigDecimal d = new BigDecimal(value);
gen.writeNumber(d.toPlainString());
}
}
}
The trick is to register a custom Serializer for your Double value. This way, you can control what you want.
I am using the BigDecimal value to create a String representation of your Double. The output then becomes (for the specific example):
{"type":"Int","min":100.0,"max":10200000000}
I hope that solves your problem.
Artur
Feature.WRITE_BIGDECIMAL_AS_PLAIN
set this for your Object Mapper
I know I am answering late, but something I faced may help other
While converting a BigDecimal I have faced below is working
mapper = mapper.setNodeFactory(JsonNodeFactory.withExactBigDecimals(true));
while this is not working for me
mapper.configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true);
Update for Jakson 2.9.10:
Property WRITE_BIGDECIMAL_AS_PLAIN replaced to com.fasterxml.jackson.core.JsonGenerator. You could use:
mapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
If you are using ValueToTree then no need of any factory settings. only problem with ValueToTree is it is converting as TextNode (String Fromat), So if you have any logic based on ObjectNodes it will not work
You should use
mapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
To avoid scientific notation on floating numbers.
You can find an example below.
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
String test ="{\"doubleValue\" : 0.00001}";
try {
System.out.println(mapper.readTree(test).toPrettyString());
} catch (JsonProcessingException e) {
e.printStackTrace();
}
Output
{
"doubleValue" : 0.00001
}
I have an existing class hierarchy that looks like this:
public interface Service {
String getId();
String getName();
}
public class FooTask extends AbstractTask {
private final static ObjectMapper JSON_MAPPER = new ObjectMapper();
static {
JSON_MAPPER.registerModule(new SimpleModule().addDeserializer(Result.class, new ResultDeserializer());
}
public FooTask(Service service) {
super(service);
}
#Override public Result call() throws Exception {
InputStream json = <... execute some code to retrieve JSON ...>
Result result = JSON_MAPPER.readValue(json, Result.class);
}
private static class ResultDeserializer {
#Override public Result deserialize(JsonParser parser, DeserializationContext ctx) throws IOException {
//
// Need to access service#getId() down here... but we're in a static nested class
// and I don't know how to access it. Is there some way to pass that info via the DeserializationContext?
//
<... Deserialization logic ...>
}
}
}
I need to pass some information to the deserializer at deserialization time but I cannot find a way to pass some contextual information to the deserializer at deserialization time. Is this possible? If so, how? I would prefer to not have to allocate a new ObjectMapper every time the FooTask is instantiated or #call() method is invoked.
So I came up with a solution... no idea if it is the ideal solution, but it is a solution - basically I first create an instance of InjectableValues:
private InjectableValues newInjectableValues() {
return new InjectableValues.Std()
.addValue("providerId", service.getId())
}
Then I get a new ObjectReader instance from the ObjectMapper and use that to perform the deserialization:
JSON_MAPPER.reader(newInjectableValues()).withType(Result.class).readValue(inputStream)
Down in the actual Deserializer I use this method to retrieve the values provided by the InjectableValues:
ctx.findInjectableValue("providerId", null, null);
I was experimenting with Jackson 2.0 mixins to serialize a class with no annotations.
Simplified source code below. Note that I'm not using getters/setters, but it seemed like I should still be able to use mixins according to the documentation.
public class NoAnnotation {
private Date created;
private String name;
// make one with some data in it for the test
static NoAnnotation make() {
NoAnnotation na= new NoAnnotation();
na.created = new Date();
na.name = "FooBear";
return na;
}
// my Mixin "class"
static class JacksonMixIn {
JacksonMixIn(#JsonProperty("created") Date created,
#JsonProperty("name") String name)
{ /* do nothing */ }
}
// test code
public static void main(String[] args) throws Exception {
NoAnnotation na = NoAnnotation.make();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixInAnnotations(NoAnnotation.class, JacksonMixIn.class);
String jsonText = objectMapper.writeValueAsString(na);
System.out.println(jsonText);
}
}
When I run main I get
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class com.flyingspaniel.so.NoAnnotation and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.SerializationFeature.FAIL_ON_EMPTY_BEANS) )
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:51)
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:25)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:108)
at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:2407)
at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:1983)
at com.flyingspaniel.so.NoAnnotation.main(NoAnnotation.java:49)
When I follow the instructions in the Exception and add a line
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
I no longer get an exception, but the result is an empty JSON object, {}.
If I make the fields public it works, but that is not something I want to do, as it's not a reasonable object design.
I'm guessing that I am leaving out a basic "setThis" step somewhere, but don't know what. How can I get mixins to work in this situation?
I figured it out. If you want to access private fields, you need to play with the Visibility by adding the following line:
objectMapper.setVisibilityChecker(VisibilityChecker.Std.defaultInstance()
.withFieldVisibility(Visibility.ANY));
For protected fields, you could also use Visibility.PROTECTED_AND_PUBLIC.
Full example
// test code
public static void main(String[] args) throws Exception {
NoAnnotation na = NoAnnotation.make();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixInAnnotations(NoAnnotation.class, JacksonMixIn.class);
objectMapper.setVisibilityChecker(VisibilityChecker.Std.defaultInstance()
.withFieldVisibility(Visibility.ANY));
String jsonText = objectMapper.writeValueAsString(na);
System.out.println(jsonText);
}
If you want use the annotation mixin the correct way to declare it is:
static class JacksonMixIn {
#JsonProperty Date created;
#JsonProperty String name;
}
When done in this way you can control the fields to serialize simply including/excluding them from the mix in.
As mentioned in your self-answer, changing the field visibility checker will resolve this situation. As an alternative to modifying the ObjectMapper, this can be done with a purely annotation-based solution by using the #JsonAutoDetect annotation:
#JsonAutoDetect(fieldVisibility = Visibility.ANY)
static class JacksonMixIn {
JacksonMixIn(#JsonProperty("created") Date created,
#JsonProperty("id") int id)
{ /* do nothing */ }
}