Validate a JSON and provide a custom validation report - java

I have an application that receives a JSON and upon receiving it should validate it. The validation is consisted out of several steps.
If the received object is a properly formatted JSON
If all the required fields are present
If all the fields constraints are satisfied (ie. string sizes)
If the conditional dependencies are satisfied (if some field has value A, then another field should have value B)
Based on the described requirements, if any of the constraints is violated, I should generate my own, specifically formatted, validation report object.
I have considered several ways how to do this, but I am not really happy with any, and hoping to get an advice from you guys, how this could be done. What I have considered:
Idea #1: Jackson + JSR303 bean validation
#CustomConstraintIfAThenB // this should emulate the conditional dependency
public class Message {
#JsonProperty(value = "a", required = true)
#Size(min = 3, max = 8)
private String a;
#JsonProperty(value = "b", required = true)
#Size(min = 5, max = 9)
private String b;
// ...
}
The problem I found with this approach is when generating my report, because first I need to use the Jackson's ObjectMapper to try to create my POJO. If this throws JsonProcessingException I should parse it, and create my report. If doesn't throw, only then I can validate with my bean validator (ie. Hibernate).
MyReport report = null;
Message m = null;
// first Jackson validation
try {
m = objectMapper.treeToValue(json, Message.class);
} catch (JsonProcessingException e) {
report = myExceptionParser.parse(e);
}
// second bean (field) validation
Set<ConstraintViolation<Message>> violations = validator.validate(m);
if (violations.size() > 0) {
report = myViolationsParser.parse(violations);
}
Big problem with this is the actual parsing through the JsonProcessingException to create my custom report. And additionally parse through the violations. I should write two parsers, one of which (the one that parses the exception) would probably be extremely clumsey.
Idea #2: Validation against JSON schema
I could write a JSON schema and then validate an incoming JSON against that schema. The up side for this is that I would have a unique, consistent report generated by the JSON Schema Validator I am using, and I should only write one parser for it. For example if I were to use fge-validator:
JsonSchema schema = JsonSchemaFactory.byDefault().getJsonSchema(mySchema);
ProcessingReport report = mySchema.validate(json);
// only have one parser to parse the report to my custom report
MyReport = myReportParser.parse(report);
The problem is with this that JSON Schema v4 gets very clumsy when dealing with conditional dependencies, as there are no IF-THEN-ELSE keywords. And I couldn't find a library that would support JSON Schema v7. Also, modifying a large JSON Schema can get tricky and very prone to errors. In that context also, class annotations would be better.
Is there any 3rd way to do the JSON validation and create a custom report out of it, that is moderately elegant and not clumsy as writing two parsers (one of which parses the exception)?
Please also note that my JSON files can easily have 200+ fields, so writing a custom deserializer field-by-field is probably not an option.
I would like to get some advises on this, as I can't really find a reasonable way to do this.

Related

Duplicate Tag accepted by JAXB

In our application, User is sending an XML like below:
<Decl>
<MessageHeader>
<A>Info</A>
<B>Info2</B>
</MessageHeader>
</Decl>
and using JABX unmarshller we are populating the java object.
But the problem is customer is asking to generate the error when he/she
send the message like below:
<Decl>
<MessageHeader>
<A>Info</A>
<B>Info2</B>
</MessageHeader>
<MessageHeader>
<A>Info3</A>
<B>Info4</B>
</MessageHeader>
</Decl>
JAXB is successfully unmarshling the xml and populating the object with second MessageHeader Object. Please note MessageHeader is not List type.
Is there any way to stop JAXB to behave like this and it should throw ParsingException. We cannot use schema validation of xml before parsing due to some reason. But is there any way that we can instruct JAXB parser to check target type is list or not and throw the error.
Thanks a lot in advance.
JAXB is a technology for generating Java classes for an XML Schema model. Therefore, any validation that JAXB performs is going to use XML Schema validation.
Is there any way to stop JAXB to behave like this and it should throw ParsingException
No. Not unless you enable schema validation in your JAXB unmarshaller.
We cannot use schema validation of xml before parsing due to some reason.
You should either supply the reason, or not mention it. It's a very strange rule, given that JAXB is an XML Schema technology and Java has native support for XSD validation.
Please note MessageHeader is not List type.
A List type is for simple types. MessageHeader is based on a complex type.
I recommend that you ignore the rule that says 'no schema validation'. Set maxOccurs=1 on MessageHeader and enable schema validation on the JAXB unmarshaller.
As already pointed out, the way to go is to use schema validation. Generate a schema and set maxOccurs="1" on MessageHeader. You can set the schema with:
unmarshaller.setSchema(schema);
If for some strange reason you absolutely cannot use schema validation you could do an ugly hack to throw an exception when a duplicated MessageHeader is processed with an implementation of Unmarshaller.Listener
unmarshaller.setListener(new Unmarshaller.Listener() {
boolean messageHeaderAlreadyPresent = false;
#Override
public void beforeUnmarshal(Object target, Object parent) {
// When <Decl> is encountered, set to false
if (target instanceof Decl) {
messageHeaderAlreadyPresent = false;
} else if (target instanceof MessageHeader) {
if (messageHeaderAlreadyPresent) {
throw new RuntimeException("duplicate MessageHeader");
}
messageHeaderAlreadyPresent = true;
}
}
});
Although this should work, I insist that you should reconsider schema validation.
Also note that the unmarshaller silently ignores some events, for example when encounters en element that is not mapped. That's not your case, because there is no mapping problem in your use case, but, for example if you received the message:
<Decl>
<MessageHeader>
<A>Info1</A>
<C>Info3</C>
</MessageHeader>
</Decl>
with element <C> instead of <B>, you would end up with values A = Info1 and B = null, as JAXB would silently ignore the element <C>.
You can capture these events and throw an exception setting a ValidationEventHandler to return false
unmarshaller.setEventHandler(new ValidationEventHandler() {
#Override
public boolean handleEvent(ValidationEvent event) {
// log event ....
return false;
}
});

Java validation of JSON payload: return errors as a tree

I have a webapp (Play framework 2.x, Java) that receives JSON payloads as input.
I have input payloads in different shapes like:
{
files: [{id: 1,name: null}}
requiredAttribute: null,
}
I want to output errors in this form, similar to the input:
{
files: [{name: "name can't be null"}}
requiredAttribute: "requiredAttribute can't be null",
}
I'd like to know how I can output errors in this form with Java without too much pain.
I know I'll loose the ability to output multiple errors per field and I'm fine with that.
I'm ok using any external library as long as it's easy to declare the constraints on the fields, so using something like Java validation and validation constraints annotations would be nice. But I wasn't able to find any support for this kind of stuff so far. Any idea how it could be done with Play or Java validation or Jackson?
Using bean validation, you can achieve this by calling validate() yourself and processing a collection of Set<ConstraintViolation<T>>.
You first need to get a Validator object. There may be ways to do this better, but one way is to use the factory (used this in the past, it worked with a Hibernate validator dependency on the class path):
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Then use the validator to retrieve a set of constraint violations (Assuming a generic type T for a bean class with the relevant constraint annotations):
Set<ConstraintViolation<T>> constraintViolations = validator.validate(myBean);
Map<String, String> fieldErrors = new HashMap<>();
for (ConstraintViolation<T> violation : constraintViolations) {
String message = violation.getMessage();
String field = violation.getPropertyPath().toString();
fieldErrors.put(field, message);
}
Note that for nested bean classes, you'll get a dot-separated "path" for field names.

How to marshall POJO to JSON using JETTISON?

I have done the marshalling of a JaxB java object to Json using JETTISON. But I can not marshall a simple java object (which has no annotations in it) to Json using JETTISON. I know it is possible to do it by using GSON or MOXy or some other providers.
But I like to get clear "Can we do it using JETTISON?"., If we can, How to do it?
Thanks in Advance.
Don't waste your time, this is simply not what Jettison was designed to do. Conceivably, it would have been possible to instantiate a JSONObject with your POJO and serialize it that way, but there are some issues with its code that make this next to impossible:
It requires passing in the names of the fields that will be included in the JSON.
It can only process public properties of the supplied object.
Not to mention it cannot handle nesting of any kind. Take a look at this lovely code:
Class c = object.getClass();
for (int i = 0; i < names.length; i += 1) {
try {
String name = names[i];
Field field = c.getField(name);
Object value = field.get(object);
this.put(name, value);
} catch (Exception e) {
/* forget about it */
}
}
Yep, thats the code in the constructor JSONObject(Object, String[]). I'm sure you will see the problems with it (raw access to generic objects, can only access public fields, sloppy exception handling). All in all - very bad 'serialization' code.
I know its probably not what you want to hear, but if you want to convert regular Java objects to JSON then you might want to stick with one of the more general-purpose libraries.
JAXB (JSR-222) is configuration by exception and only requires annotations where you need to override the default XML representation (Jettison converts XML StAX events to/from JSON). Instead of #XmlRootElement you can wrap your object in an instance of JAXBElement.
http://blog.bdoughan.com/2012/07/jaxb-no-annotations-required.html

Jackson - Required property?

I'm using Jackson's readValue() method on an object mapper to read from a JSON file and convert it into my java object.
eg.
mapperObject.readValue( node, MyTargetClass.class )
Are there any annotations that I can set on MyTargetClass to enforce required attributes? For example, if I have a JSON object with properties ABC,DEF and GHI, and my Json is the following
{
"ABC" : "somevalue"
"DEF" : "someothervalue"
}
I want it to fail somehow, and only succeed on the readValue if it contained ABC, DEF and GHI.
You can mark a property as required with the #JsonProperty(required = true) annotation, and it will throw a JsonMappingException during deserialization if the property is missing or null.
Edit: I received a downvote for this without comment. I'd love to know why, since it does exactly the right thing.
Jackson does not include validation functionality, and this is by design (i.e. that is considered out-of-scope). But what is usually used is Bean Validation API implementation.
The nice thing about this is decoupling between data format handling, and validation logic.
This is what frameworks like DropWizard use; and it's the direction JAX-RS (like Jersey) are taking things for JAX-RS 2.0.
If you want to make sure a json field is provided, you have to use the #JsonProperty(value = "fieldName", required = true) annotation as a parameter to the constructor. But this is not enough, also the Constructor should have #JsonCreator annotation.
For example, if you have a field named 'endPoint' and you want o make sure it is provided in the JSON file, then the following code will throw an exception if it is not provided.
#JsonCreator
public QuerySettings(#JsonProperty(value = "endPoint", required = true) String endPoint) {
this.endPoint = endPoint;
}
I found this link helpful to understand the Jackson annotations. It also well explains why required=true is not enough and counter-intuitive to its name.
If you are neither satisfied with using #JsonProperty(required = true) as it works only with #JsonCreator nor with the use of bean validation then one more way of tackling it would be to catch this in your setter methods for the relevant variables.
You can simply check if the variable is null before setting it and throw an IllegalArgumentException or NullPointerException (as preferred by few people)
Note: It depends on how your POJO is defined too, so please make sure that it is going the setter method route for this solution to work.

Is there a way to create the bean class from a json response

Converting JSON to Java
The above question is with reference to what has been described on the above thread. There are so many API(s) which provide the flexibility to return responses either in XML or JSON. **I would like to know if there is a way to automatically construct the java bean corresponding to a JSON response. **
lets say you get an object like
[
{
"name":"Java 6 Greatest Hits",
"Author":"Jim Bob Jones",
"price":10.25
},
{
"name":"How to raise a goat",
"Author":"Sir Paxton",
"price":55.97
},
{
"name":"Snow - It is cold",
"Author":"Dr. White",
"price":9.99
}
]
And you want a class like
public class Book{
private String author;
private String name;
private Number price
}
with getters and setters
One option is to use a service like JSONGen, which will create that class. You need to use it first, and include the generated code in your project.
Another option could be dynamically generate the class using javassist or CGLib, but that class would be useless unless you use reflection to access its members, so even if it would be a class, it will behave like a really annoying Map. In no way will be better that simple using JSONObject
seems a simple Message Type Entity not meet you requirement ?
if you want convert a json to an existed and known java bean class,
many lib can do so, like
http://json-lib.sourceforge.net/apidocs/net/sf/json/class-use/JSONObject.html
JSONObject.toBean(JSONObject jsonObject, Class beanClass)
Creates a bean from a JSONObject, with a specific target class.
btw, if you are communicating with restful webservice, org.springframework.web.client.RestTemplate will help you get direct bean result
insteadof json.
if class does not exists, you need program with java reflect mechanism.
try use CGLIB ,http://cglib.sourceforge.net/, dynamic create some class like BeanMap. i wrote a simple sample,
but be ware, opearting class byte is hard and you may meet strange trouble with JVM . Strongly not encourage to do so.
public static BeanMap generateBean(JSONObject json) {
BeanGenerator generator = new BeanGenerator();
Iterator keys = json.keys();
while (keys.hasNext()) {
Object key = keys.next();
Object value = json.get(key);
Class keyClass = guessValueClass(value);
generator.addProperty(key.toString(), keyClass);
}
Object result = generator.create();
BeanMap bean = BeanMap.create(result);
keys = json.keys();
while (keys.hasNext()) {
Object key = keys.next();
Object value = json.get(key);
bean.put(key, value);
}
return bean;
}
/**
* TODO fix guess
*/
static Class guessValueClass(Object value) {
try {
Integer.parseInt(value.toString());
return Integer.class;
} catch (NumberFormatException e1) {
}
try {
Double.parseDouble(value.toString());
return Double.class;
} catch (NumberFormatException e1) {
}
return String.class;
}
I believe the main issue here is that the JSON response lacks type information and last time I checked :-) in Java you need to declare the type of a class property. So some heuristics will be needed to infer the type form the value in the JSON response.
For a related question here in SO have a look at: Generate Java class from JSON?
Yes check out http://flexjson.sourceforge.net
If you're wanting to generate Java classes from JSON, perhaps you could try Jackson. It provides a lot of JSON-related functionality, including the ability to generate bytecode from arbitrary JSON. See this blog post for details.
If you're using Jackson (the most popular library there), try
https://bitbucket.org/astav/jsontojava/wiki/Home
Its open source and anyone should be able to contribute.
Summary
A JsonToJava source class file generator that deduces the schema based on supplied sample json data and generates the necessary java data structures.
It encourages teams to think in Json first, before writing actual code.
Features
Can generate classes for an arbitrarily complex hierarchy (recursively)
Can read your existing Java classes and if it can deserialize into those structures, will do so
Will prompt for user input when ambiguous cases exist

Categories