I'm trying to serialize a custom Exception in Java using writeValueAsString() method from Jackson library. I intend to send it by HTTP to another machine. This is working partialy because not all fields are included in JSON after serialize. The top level exception Throwable implements Serializable interface, and also has some constructors that add info about what is to be serialized. I suppose the truth is somewhere here. Please help with some advices. Here is my custom exception code:
import java.io.Serializable;
public class MyException extends RuntimeException{
private static String type = null;
private static String severity = null;
// somewhere on google I red that should use setters to make serialize work
public static void setType(String type) {
MyException.type = type;
}
public static void setSeverity(String severity) {
MyException.severity = severity;
}
public MyException(String message) {
super(message);
}
}
somewhere in code I use:
MyException exc = new MyException("Here goes my exception.");
MyException.setType(exc.getClass().getSimpleName());
MyException.setSeverity("Major");
throw exc;
and in other place I have:
ObjectMapper mapper = new ObjectMapper();
try {
responseBuilder.entity(mapper.writeValueAsString(MyException) );
}
catch (JsonGenerationException e) {e.printStackTrace(); }
catch (JsonMappingException e) {e.printStackTrace(); }
catch (IOException e) { e.printStackTrace(); }
The result JSON object is:
{"cause":null,"message":"Here goes my exception.","localizedMessage":"Here goes my exception.","stackTrace":[{...a usual stack trace...}]}
Here I also expect to see my type and severity fields.
I made type and severity non-static, and it seems to be working fine. I used the following code, and I see both type and severity in the serialized output.
public class MyException extends RuntimeException
{
private String type = null;
private String severity = null;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getSeverity() {
return severity;
}
public void setSeverity(String severity) {
this.severity = severity;
}
public MyException(String message) {
super(message);
}
}
... and
MyException exc = new MyException("Here goes my exception.");
exc.setType(exc.getClass().getSimpleName());
exc.setSeverity("Major");
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(exc));
Hope this helps!
Related
Trying to deserialize/serialize JSON into Java beans I've created. Really new to Jackson and this endeavor, so bear with me. I have the following:
{
"foo": {
"firstBlock": {
"myValue": 1,
"someBool": true,
"stringValue": "OK"
},
"anotherBlock": {
"values": [
{
"yikes01": 42
},
{
"yikes02": 215
}
],
"myInt": 64,
"logging": "Yes"
}
}
}
My Java beans are broken down into several as the objects in the JSON are used repeatedly, so it would be:
#JsonRootName("foo")
public class FooBean {
private FirstBlockBean firstBlock;
private AnotherBlockBean anotherBlock;
#JsonGetter("firstBlock")
public FirstBlockBean getFirstBlock() { return firstBlock; }
#JsonSetter("firstBlock")
public void setFirstBlock(FirstBlockBean firstBlock) { this.firstBlock = firstBlock; }
#JsonGetter("anotherBlock")
public AnotherBlockBean getAnotherBlock() { return anotherBlock; }
#JsonSetter("firstBlock")
public void setAnotherBlock(AnotherBlockBean anotherBlock) { this.anotherBlock = anotherBlock; }
}
#JsonRootName("firstBlock")
public class FirstBlockBean {
private int myValue;
private Boolean someBool;
private String stringValue;
#JsonGetter("myValue")
public int getMyValue() { return myValue; }
#JsonSetter("myValue")
public void setMyValue(int myValue) { this.myValue = myValue; }
#JsonGetter("someBool")
public Boolean getSomeBool() { return someBool; }
#JsonSetter("someBool")
public void setSomeBool(Boolean someBool) { this.someBool = someBool; }
#JsonGetter("stringValue")
public String getStringValue() { return stringValue; }
#JsonSetter("someBool")
public void setStringValue(String stringValue) { this.stringValue = stringValue; }
}
...and AnotherBlockBean class implemented in similar fashion (omitted for brevity.) I'm using Jackson for this, and my question is - is there a mechanism in Jackson for serializing and deserializing for this case? Ideally I'd like something along the lines of (pseudo-code below because I've not been able to surface anything via Google searches or searches on here):
// Assume "node" contains a JsonNode for the tree and foo is an uninitialized FooBean class object.
JsonHelper.deserialize(node, FooBean.class, foo);
At this point I'd be able to read the values back:
int i = foo.getFirstBlock().getMyValue();
System.out.println("i = " + i); // i = 1
Similarly I'd like to be able to take the foo instance and serialize it back into JSON with another method. Am I dreaming for wanting this sort of built-in functionality or does it exist?
The main class when working with Jackson is the ObjectMapper. It has a lot of options, take a look at the available methods.
This is an example of a typical helper class that uses the ObjectMapper to convert between Java objects and Strings.
public class JsonHelper {
private ObjectMapper objectMapper;
public JsonHelper(){
this.objectMapper = new ObjectMapper();
// Your mapping preferences here
this.objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE);
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
this.objectMapper.setSerializationInclusion(Include.NON_NULL);
this.objectMapper.configure(Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
}
public String serialize(Object object) {
try {
return this.objectMapper.writeValueAsString(object);
} catch (Exception e) {
// TODO Handle exception
return null;
}
}
public <T> T deserialize(String json, Class<T> clazz) {
try {
return this.objectMapper.readValue(json, clazz);
} catch (Exception e) {
// TODO Handle exception
return null;
}
}
public <T> T deserialize(String json, TypeReference<T> valueTypeRef) {
try {
return this.objectMapper.readValue(json, valueTypeRef);
} catch (Exception e) {
// TODO Handle exception
return null;
}
}
}
Some tips:
If the name of the getter and setter methods follows the usual convention, you can omit the #JsonGetter and #JsonSetter annotations and just use the #JsonProperty annotation in the field declaration
If the name of the java field is equal to the node name in the JSON, you can also omit the #JsonProperty annotation (Jackson will map JSON nodes and Java fields with matching names).
I have created a new exception class in my Dropwizard service that extends BadRequestException.
public class CustomBadRequestException extends BadRequestException {
private static final long serialVersionUID = 1L;
private List<ValidationFailureDto> validationFailures;
public CustomBadRequestException() {
super();
}
public CustomBadRequestException(final List<ValidationFailureDto> validationFailures) {
super();
this.validationFailures = validationFailures;
}
#ApiModelProperty(value = "List of validationFailures")
public List<ValidationFailureDto> getValidationFailures() {
return validationFailures;
}
}
When I throw that exception at first I was only getting back the deserialised BadRequestException, minus the additional property (validationFailures)
{
code: "400",
message: "Bad request"
}
This is because Dropwizard's internals have a default exception mapper that allows Jetty/Jackson to understand domain exceptions and how to send the appropriate HTTP response.
To overcome this you can implement your own ExceptionMapper class and register it with Dropwizard.
public class CustomBadRequestExceptionMapper implements ExceptionMapper<SamplePackOrderBadRequestException> {
/**
* Allows jackson to deserialise custom exceptions and its properties to JSON response
*
* #param exception exception
* #return response object
*/
#Override
public Response toResponse(final SamplePackOrderBadRequestException exception) {
if (exception instanceof SamplePackOrderBadRequestException) {
SamplePackOrderBadRequestException samplePackOrderBadRequestException
= (SamplePackOrderBadRequestException) exception;
return Response
.status(400)
.entity(samplePackOrderBadRequestException)
.build();
}
return Response.status(400).build();
}
}
However this issue with this is that it deserializes super (Throwable), so you get every single inherited property added in the response which I do not want.
To combat this I tried adding Jackson annotations like so:
#JsonIgnoreProperties(value = "stackTrace")
This is not an optimal solution as there are several properties other than stackTrace that I will need to ignore.
So to summarise, how can I get Dropwizard to properly deserialize my CustomException class without all the additional clutter that I do not need?
I think the easier option is to transform exception to a Error bean and return it as shown below.
public class CustomBadRequestExceptionMapper implements ExceptionMapper<SamplePackOrderBadRequestException> {
#Override
public Response toResponse(final SamplePackOrderBadRequestException exception) {
if (exception instanceof SamplePackOrderBadRequestException) {
SamplePackOrderBadRequestException ex
= (SamplePackOrderBadRequestException) exception;
return Response
.status(400)
.entity(new ErrorBean(400,ex.getMessage,ex.getgetValidationFailures()))
.build();
}
return Response.status(400).build();
}
}
And ErrorBean.java
public static class ErrorBean{
private int code;
private String message;
private List<ValidationFailureDto> failures;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public List<ValidationFailureDto> getFailures() {
return failures;
}
public void setFailures(List<ValidationFailureDto> failures) {
this.failures = failures;
}
}
Problem:
I am deserializing enums with Jackson that don't match up with their name in the code, below is a sample of json.
{
"thing1": {"foo": "cool-guy"},
"thing2": {"foo": "loser-face"}
}
Here is the enum, I will explain the interface later.
enum Foo implements HasText {
COOL_GUY("cool-guy"), LOSER_FACE("loser-face"), // etc...
private String text;
private Foo(String text) {
this.text = text;
}
#Override
public String getText() {
return text;
}
}
I know how to solve this issue for each enum individually by making a deserializer (below) and the annotation #JsonDeserialize(using = FooDeserializer .class) on the setter method for foo.
public class FooDeserializer extends JsonDeserializer<Enum<Foo>> {
#Override
public Foo deserialize(JsonParser p, DeserializationContext context) throws Exception {
if (p.getCurrentToken().equals(JsonToken.VALUE_STRING)) {
String jsonText = p.getText();
Stream<Foo> stream = Arrays.asList(Foo.values()).stream();
return stream.filter(a -> a.getText().equals(jsonText.toLowerCase())).findAny().get();
}
throw context.mappingException(Foo.class);
}
}
Question:
Is there a way to do this abstractly? That's why I added the HasText interface to all my enums in hopes there was a way to do something like this:
public class EnumWithTextDeserializer<T extends Enum<T> & HasText> extends JsonDeserializer<T> {
#Override
public T deserialize(JsonParser p, DeserializationContext context) throws Exception {
if (p.getCurrentToken().equals(JsonToken.VALUE_STRING)) {
final String jsonText = p.getText();
final Stream<T> stream = Arrays.asList(runtimeClass().getEnumConstants()).stream();
return stream.filter(a -> a.getText().equals(jsonText.toLowerCase())).findAny().get();
}
throw context.mappingException(runtimeClass());
}
#SuppressWarnings("unchecked")
private Class<T> runtimeClass() {
ParameterizedType superclass = (ParameterizedType) getClass().getGenericSuperclass();
return (Class<T>) superclass.getActualTypeArguments()[0];
}
}
The compile won't let me annotate the setter method (#JsonDeserialize(using = EnumWithTextDeserializer.class)) with this class though because
Type mismatch: cannot convert from Class<EnumWithTextDeserializer> to Class<? extends JsonDeserializer<?>>".
Really, all I want to be able to do is deserialize these enums based on the getText() method.
In order to deserialize, you can specify your String value using #JsonValue.
public enum FooEnum implements WithText {
AWESOME("awesome-rad"),
NARLY("totally-narly");
private final String text;
FooEnum(String text) {
this.text = text;
}
#Override
#JsonValue
public String getText() {
return text;
}
}
Then executing this code to serialize/deserialize
ImmutableMap<String, FooEnum> map = ImmutableMap.of("value", FooEnum.AWESOME, "value2", FooEnum.NARLY);
final String value;
try {
value = objectMapper.writeValueAsString(map);
} catch (JsonProcessingException e) {
throw Throwables.propagate(e);
}
Map<String, FooEnum> read;
try {
read = objectMapper.readValue(value, new TypeReference<Map<String, FooEnum>>() {});
} catch (IOException e) {
throw Throwables.propagate(e);
}
I get:
read = {LinkedHashMap#4627} size = 2
0 = {LinkedHashMap$Entry#4631} "value1" -> "AWESEOME"
1 = {LinkedHashMap$Entry#4632} "value2" -> "NARLY"
I have to call a REST WS which can return any JSON structure out of which I want only id and name which every Service will return. The code which I wrote is:- `
public class RestWebServiceCaller {
public static <T> ArrayList<T> callGETRestWebService(String url,String mediaType, Class<T> responseType) throws Exception {
ClientRequest request=new ClientRequest(url);
request.accept(mediaType);
ClientResponse<T> response=request.get(responseType);
return (ArrayList<T>)response.getEntity();
}
public static void main(String[] args) {
try {
ArrayList<ScopeResponse> response=callGETRestWebService("http://10.63.8.220:9080/get",MediaType.APPLICATION_JSON
,ScopeResponse.class);
ArrayList<ScopeResponse> scope=(ArrayList<ScopeResponse>) response;
System.out.println(scope.get(0).getName());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
public class ScopeResponse {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
But this line System.out.println(scope.get(0).getName()); is giving Class cast Exception.
My Requirement is to have a Custom class(with Id and Name properties) which can convert every Rest call to the List.
Thanks in advance.
I want to use the default JAX-RS response deserializer.
Here is my POJO
#JsonIgnoreProperties(ignoreUnknown = true)
public class Email
{
private String mFrom;
private List<String> mTo;
private List<String> mCc;
private List<String> mBcc;
private String mSubject;
private String mText;
public void setFrom(String from)
{
mFrom = from;
}
#JsonProperty("from")
public String getFrom()
{
return mFrom;
}
#JsonProperty("to")
public List<String> getTo()
{
return mTo;
}
public void setTo(List<String> to)
{
mTo = to;
}
#JsonProperty("carbon_copy")
public List<String> getCc()
{
return mCc;
}
public void setCc(List<String> cc)
{
mCc = cc;
}
#JsonProperty("blind_carbon_copy")
public List<String> getBcc()
{
return mBcc;
}
public void setBcc(List<String> bcc)
{
mBcc = bcc;
}
}
This my JAX-RS code.
#GET
#Produces("application/json", "application/xml", "text/xml")
public Response getEmails() {
List<Email> emails = getEmails(); //returns list of emails
return Response.ok(emails).build();
}
output
[{"from":"example#isp.com","to":[ ],"cC":[ ],"bCc":[ ],"subject":"my subject","text":"email from admin"}]
I want to change "cC" to the "carbon_copy". I want to solve this using the JAX-RS Response. How do I get JAX-RS to use the jackson annotated property name. Do I need to override something?
My current implementation i did the following.
public class JsonDeserializer
{
private static ObjectMapper mMapper;
static
{
mMapper = new ObjectMapper();
mMapper.setSerializationInclusion(Inclusion.NON_NULL);
}
#SuppressWarnings({ "unchecked", "rawtypes" })
public static <T> T fromInputStream(InputStream is, Class t)
{
try
{
return (T) mMapper.readValue(is, t);
}
catch (JsonParseException e)
{
e.printStackTrace();
}
catch (JsonMappingException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
catch (ClassCastException e)
{
e.printStackTrace();
}
return null;
}
}
Response.ok(JsonDeserializer.toJson(emails)).build();
Is there away to do it without creating another class to handle the deserialization process.
Mix-in can help you resolve this. You need to create an abstract class say "EmailExpanded" that has the property something like this:
#JsonProperty("carbon_copy")
public abstract List<String> getCc();
Then add that mixin:
emailExpandMapper = new ObjectMapper();
emailExpandMapper.getSerializationConfig().addMixInAnnotations(
Email.class, EmailExpanded.class);
emailExpandMapper.getSerializationConfig().setSerializationInclusion(
Inclusion.NON_NULL);
Later in the code while you send the response:
emailExpandMapper.writeValueAsString(emails)
You can read more about Mixins in the web.