package com.fasterxml.jackson.databind.exc; import java.io.IOException; import java.util.*; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.*; /** * Unit tests for verifying that simple exceptions can be serialized. */ public class ExceptionSerializationTest extends BaseMapTest { @SuppressWarnings("serial") @JsonIgnoreProperties({ "bogus1" }) static class ExceptionWithIgnoral extends RuntimeException { public int bogus1 = 3; public int bogus2 = 5; protected ExceptionWithIgnoral() { } public ExceptionWithIgnoral(String msg) { super(msg); } } // [databind#1368] static class NoSerdeConstructor { private String strVal; public String getVal() { return strVal; } public NoSerdeConstructor( String strVal ) { this.strVal = strVal; } } /* /********************************************************** /* Tests /********************************************************** */ private final ObjectMapper MAPPER = new ObjectMapper(); public void testSimple() throws Exception { String TEST = "test exception"; Map<String,Object> result = writeAndMap(MAPPER, new Exception(TEST)); // JDK 7 has introduced a new property 'suppressed' to Throwable Object ob = result.get("suppressed"); if (ob != null) { assertEquals(5, result.size()); } else { assertEquals(4, result.size()); } assertEquals(TEST, result.get("message")); assertNull(result.get("cause")); assertEquals(TEST, result.get("localizedMessage")); // hmmh. what should we get for stack traces? Object traces = result.get("stackTrace"); if (!(traces instanceof List<?>)) { fail("Expected a List for exception member 'stackTrace', got: "+traces); } } // to double-check [databind#1413] public void testSimpleOther() throws Exception { JsonParser p = MAPPER.getFactory().createParser("{ }"); InvalidFormatException exc = InvalidFormatException.from(p, "Test", getClass(), String.class); String json = MAPPER.writeValueAsString(exc); p.close(); assertNotNull(json); } // for [databind#877] @SuppressWarnings("unchecked") public void testIgnorals() throws Exception { ExceptionWithIgnoral input = new ExceptionWithIgnoral("foobar"); input.initCause(new IOException("surprise!")); // First, should ignore anything with class annotations String json = MAPPER .writerWithDefaultPrettyPrinter() .writeValueAsString(input); Map<String,Object> result = MAPPER.readValue(json, Map.class); assertEquals("foobar", result.get("message")); assertNull(result.get("bogus1")); assertNotNull(result.get("bogus2")); // and then also remova second property with config overrides ObjectMapper mapper = new ObjectMapper(); mapper.configOverride(ExceptionWithIgnoral.class) .setIgnorals(JsonIgnoreProperties.Value.forIgnoredProperties("bogus2")); String json2 = mapper .writeValueAsString(new ExceptionWithIgnoral("foobar")); Map<String,Object> result2 = mapper.readValue(json2, Map.class); assertNull(result2.get("bogus1")); assertNull(result2.get("bogus2")); // and try to deserialize as well ExceptionWithIgnoral output = mapper.readValue(json2, ExceptionWithIgnoral.class); assertNotNull(output); assertEquals("foobar", output.getMessage()); } // [databind#1368] public void testJsonMappingExceptionSerialization() throws IOException { Exception e = null; // cant deserialize due to unexpected constructor try { MAPPER.readValue( "{ \"val\": \"foo\" }", NoSerdeConstructor.class ); fail("Should not pass"); } catch (JsonMappingException e0) { verifyException(e0, "can not deserialize from Object"); e = e0; } // but should be able to serialize new exception we got String json = MAPPER.writeValueAsString(e); JsonNode root = MAPPER.readTree(json); String msg = root.path("message").asText(); String MATCH = "can not construct instance"; if (!msg.toLowerCase().contains(MATCH)) { fail("Exception should contain '"+MATCH+"', does not: '"+msg+"'"); } } }