JSON string validate with more than one class - java

In my business flow I may have JSON strings with different formats.
For example:
String jsonSring = readValue();//this JSON String may contain Teacher object or Student Object
Currently i'm using this simple method to validate if the JSON is relative to a Teacher or Student:
try{
Teacher teacher = om.readValue(jsonSring, Teacher.class);
}catch(Exception e){
Student student = om.readValue(jsonSring, Student.class);
}
Any simplified method to validate the JSON content?

Solution 1: Add a Type field:
Adding a field that specifies the object type is probably the easiest choice, but you must be able to change the objects in order to do that.
public Enum UserType { Teacher, Student, /* possibly other types */ }
public interface ISchoolMember
{
public string Name { get; }
..
public UserType Type { get; }
}
Then once you have the JSON, you can parse it to JObject and read the Type field:
public ISchoolMember Deserialize(string jsonString)
{
var o = JObject.Parse(jsonString);
return (UserType)o["Type"] switch
{
UserType.Teacher => JsonConvert.Deserialize<Teacher>(jsonString),
UserType.Student => JsonConvert.Deserialize<Student>(jsonString),
_ => throw new ArgumentException("...")
};
}
Solution 2: Check for peculiar fields.
If it is not possible to add a new field, you can check if the parsed JObject contains fields that belong only to one of the two objects:
public void DeserializeAndDoStuff(string jsonString)
{
var teacherOrStudent = JObject.Parse(jsonString);
if (teacherOrStudent["StudentId"] != null) // it is a student!
{
Student s = teacherOrStudent.ToObject<Student>();
// ... do stuff with the student object
}
else if (teacherOrStudent["TeacherId"] != null) // it is a teacher!
{
Teacher t = teacherOrStudent.ToObject<Teacher>();
// ... do stuff with the teacher object
}
else
{
throw new ArgumentException("The given object is neither a teacher or a student.");
}
}
These two methods seem more verbose than the original way, but help moving away from an Exception-based programming (that is always unadvised, as handling exceptions is very costly in term of resources).
p.s.
this implementation uses Newtonsoft.Json library, but I guess that other libraries have similar mechanisms.

Related

Creating different POJO child objects depending on criteria but share common fields

I am implementing a log management system and want the types of logs to be extendible. We get a base object parsed from JSON (from Filebeat) such as:
class LogLine {
String message
Date timestamp
String type
String source
}
Given this LogLine object, I want to be able to create different objects, which will also extend this LogLine.
class SomeLog extends LogLine {
int myfield
String username
}
class SomeOtherLog extends LogLine {
Date startTime
Date endTime
String username
String transactionID
}
So, in my current non-ideal implementation:
void parse(String s){
LogLine logLine = .....parseFromString(s)
if ( logline.type.equals('def') ){
SomeLog someLog = new SomeLog.Builder.build(logLine)
} else if ( logline.message.containts('abc') ){
SomeOtherLog someotherLog = new SomeOtherLog.Builder.build(logline)
}
}
However, as you can imagine the builders in subclasses copies the superclass LogLine object, is there anyway I can do that without copying the values as they are already subclasses? Is there a design pattern to achieve this? I would not like to rely on reflection like BeanUtils.copyProperperties
When you create a new object based on another it's a good idea to make a copy of all field. It's a best practice called defensive copying.
Since you parse a string, a defensive copy doesn't needed. Also I suppose you'll want to parse some specific fields from input string like myfield for SomeLog and startDate for SomeOtherLog. You could re-factor object creation like
LogLine result = null;
if (s.contains('type=def') {
result = SomeLog.parse(s);
} else if (trickyRegexp.mathces(s)) {
result = SomeOtherLog.parse(s);
} else {
result = LogLine.parse(s);
}
If you have many subclasses of LogLine then probably you'll want to move creation logic to a LogFactory which manages all the stuff regarding parsing string to specific object.
Introduce a factory interface for creating LogLine objects.
public interface LogLineFactory {
public LogLine createLog(LogLine logLine);
}
and use a Map for the lookup.
private Map<String, LogLineFactory > logLineFactories = new HashMap<>();
{
logLineFactories .put("def", new SomeLogFactory());
logLineFactories .put("abc", new SomeOtherLogFactory());
}
You can then ommit the if else branches using the map looup.
LogLine logLine = parseFromString(s);
LogFactory logFactory = logLineFactories.get(logLine.type);
if(logFactory != null) {
LogLine wrappedLogLine = logFactory.createLog(logLine);
}
Maybe you will need more information to create the LogLines and you have to change the interface.
public interface LogLineFactory {
public LogLine createLog(LogLine logLine, String s);
}
PS: with Java 8 you might want to use method references.
logLineFactories.put("def", SomeLog::new);
logLineFactories.put("abc", SomeOtherLog::new);

Find index of an object in a list

I have situation where I have a list(required items) that holds a table column result like:
NAME
ADDRESS
AGE
.
.
etc
In my method I get a User object that contains values for user.getName(), user.getAge() etc. I want to know the best way to ensure that every item in the list is present in the user object. The no of items in the list are variable.
public boolean isUserInfoComplete(User user, ArrayList list){
//so, if the list has AGE, the user.getAge() must have some value
}
One way I thought of is maintaining another list that holds values of every user info and checking that against my db list but that is not scalable.
It's not possible to dynamically match your method names with the list contents without reflection (which can be expensive and fragile). You may want to consider keeping your User values in a central Map cache. Here's one way to do that:
public class User {
private enum Field {
NAME,
AGE
//...
}
private Map<String, Object> values = new HashMap<>();
private void putValue(Field field, Object value) {
values.put(field.name(), value);
}
private Object getValue(Field field) {
return values.get(field.name());
}
public void setName(String name) {
putValue(Field.NAME, name);
}
public String getName() {
return (String)getValue(Field.NAME);
}
public void setAge(int age) {
putValue(Field.AGE, age);
}
public Integer getAge() {
return (Integer)getValue(Field.AGE);
}
//...
public boolean isUserInfoComplete(List<String> fields) {
return values.keySet().containsAll(fields);
}
}
You could use reflection to solve this problem if the items in the list match the getters in your User object.
For example, if AGE is in the list, you could use reflection to look for the getAge() method on the User class, call it on the object, and then check the result for null (or switch on the method return type to perform other types of checks).
Here's a starting point for you to experiment with (I haven't compiled or tested it):
public boolean isUserInfoComplete(User user, ArrayList list){
for(String attribute : list) {
String methodName = "get" + attribute.substring(0, 1).toUpperCase() + attribute.substring(1).toLowerCase();
Method method = User.class.getMethod(methodName, null);
if(method != null) {
Object result = method.invoke(user);
if(result == null) {
return false;
}
}
}
return true;
}
This seems like a case where you need reflection. This gives you the opportunity to inspect methods and field from your objects at runtime.
If you know your User-objects etc will follow a java bean standard then you will be able to use the getters for checking, though I see now problem in making your fields public final and checking directly on the fields themselves.
Take a look at https://docs.oracle.com/javase/tutorial/reflect/
You can check it using contains() while looping. This process will be very resource-consuming.
Maybe you can redesign something and simply compare two User objects? Will be faster. You can do it by providing your own implementation of equals and hashcode methods.

Java Jackson receive key with null value [duplicate]

What happens if I annotate a constructor parameter using #JsonProperty but the Json doesn't specify that property. What value does the constructor get?
How do I differentiate between a property having a null value versus a property that is not present in the JSON?
Summarizing excellent answers by Programmer Bruce and StaxMan:
Missing properties referenced by the constructor are assigned a default value as defined by Java.
You can use setter methods to differentiate between properties that are implicitly or explicitly set. Setter methods are only invoked for properties with explicit values. Setter methods can keep track of whether a property was explicitly set using a boolean flag (e.g. isValueSet).
What happens if I annotate a constructor parameter using #JsonProperty but the Json doesn't specify that property. What value does the constructor get?
For questions such as this, I like to just write a sample program and see what happens.
Following is such a sample program.
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonFoo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
// {"name":"Fred","id":42}
String jsonInput1 = "{\"name\":\"Fred\",\"id\":42}";
Bar bar1 = mapper.readValue(jsonInput1, Bar.class);
System.out.println(bar1);
// output:
// Bar: name=Fred, id=42
// {"name":"James"}
String jsonInput2 = "{\"name\":\"James\"}";
Bar bar2 = mapper.readValue(jsonInput2, Bar.class);
System.out.println(bar2);
// output:
// Bar: name=James, id=0
// {"id":7}
String jsonInput3 = "{\"id\":7}";
Bar bar3 = mapper.readValue(jsonInput3, Bar.class);
System.out.println(bar3);
// output:
// Bar: name=null, id=7
}
}
class Bar
{
private String name = "BLANK";
private int id = -1;
Bar(#JsonProperty("name") String n, #JsonProperty("id") int i)
{
name = n;
id = i;
}
#Override
public String toString()
{
return String.format("Bar: name=%s, id=%d", name, id);
}
}
The result is that the constructor is passed the default value for the data type.
How do I differentiate between a property having a null value versus a property that is not present in the JSON?
One simple approach would be to check for a default value post deserialization processing, since if the element were present in the JSON but had a null value, then the null value would be used to replace any default value given the corresponding Java field. For example:
import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.annotate.JsonMethod;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonFooToo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper().setVisibility(JsonMethod.FIELD, Visibility.ANY);
// {"name":null,"id":99}
String jsonInput1 = "{\"name\":null,\"id\":99}";
BarToo barToo1 = mapper.readValue(jsonInput1, BarToo.class);
System.out.println(barToo1);
// output:
// BarToo: name=null, id=99
// {"id":99}
String jsonInput2 = "{\"id\":99}";
BarToo barToo2 = mapper.readValue(jsonInput2, BarToo.class);
System.out.println(barToo2);
// output:
// BarToo: name=BLANK, id=99
// Interrogate barToo1 and barToo2 for
// the current value of the name field.
// If it's null, then it was null in the JSON.
// If it's BLANK, then it was missing in the JSON.
}
}
class BarToo
{
String name = "BLANK";
int id = -1;
#Override
public String toString()
{
return String.format("BarToo: name=%s, id=%d", name, id);
}
}
Another approach would be to implement a custom deserializer that checks for the required JSON elements. And yet another approach would be to log an enhancement request with the Jackson project at http://jira.codehaus.org/browse/JACKSON
In addition to constructor behavior explained in #Programmer_Bruce's answer, one way to differentiate between null value and missing value is to define a setter: setter is only called with explicit null value.
Custom setter can then set a private boolean flag ("isValueSet" or whatever) if you want to keep track of values set.
Setters have precedence over fields, in case both field and setter exist, so you can "override" behavior this way as well.
I'm thinking of using something in the style of an Option class, where a Nothing object would tell me if there is such a value or not. Has anyone done something like this with Jackson (in Java, not Scala, et al)?
(My answer might be useful to some people finding this thread via google, even if it doesn't answer OPs question)
If you are dealing with primitive types which are omittable, and you do not want to use a setter like described in the other answers (for example if you want your field to be final), you can use box objects:
public class Foo {
private final int number;
public Foo(#JsonProperty Integer number) {
if (number == null) {
this.number = 42; // some default value
} else {
this.number = number;
}
}
}
this doesn't work if the JSON actually contains null, but it can be sufficient if you know it will only contain primitives or be absent
another option is to validate the object after deserialization either manually or via frameworks such java bean validation or, if you are using spring, the spring validation support.

Using GSON to parse an object with mixed types

I'm using a webservice which unfortunately I don't have any control over, there is one element called price that can have 2 types of values, it can either be a double:
price: 263.12
or a string with a specific format:
price: "263.12;Y"
In the second case the ;N indicates that the price can be modified (ie: a discount can be added), I tried to convince the developers of the service to modify the response and send the Y or N (depending on the case) in a separate value (discount: "Y" | "N:), but they said that for now they won't do it.
Within the POJO I declared for this case, I have the following case:
private float precio;
public void setPrice(String value){
if(value.indexOf(";") == -1){
price = Float.parseFloat(value);
} else {
String[] p = value.split(";");
price = Float.parseFloat(p[0]);
}
}
public float getPrice(){return price;}
But unfortunately using:
Product obj = new Gson().fromJson(response, Product.class);
Never actually cals the setter, in the cases where the price is set as a proper double it works just fine, but where I'm receiving the string it just crashes, any suggestions on how this could be handled, worst case scenario I could create a second POJO and try/catch the object creation, but there should be a better idea and searching so far has yielded no results.
You could implement a TypeAdapter that overwrites the default serialization. You have to register that TypeAdapter for a certain class ...
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Product.class, new ProductAdapter());
Gson gson = builder.create();
... so this way any members of type Product ...
String jsonString = gson.toJson(somethingThatContainsProducts);
... will be handled by the TypeAdapter:
public class ProductAdapter extends TypeAdapter<Product> {
public Product read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
String json = reader.nextString();
// convert String to product ... assuming Product has a
// constructor that creates an instance from a String
return new Product(json);
}
public void write(JsonWriter writer, Product value) throws IOException {
if (value == null) {
writer.nullValue();
return;
}
// convert Product to String .... assuming Product has a method getAsString()
String json = value.getAsString();
writer.value(json);
}
}
Check out the Google GSON documentation for more.
Hope this helps ... Cheers!
You could write a TypeAdapter or JsonDeserializer.
You can also just rely on the fact that Gson will massage types for you and go the other way with your type:
class Pojo { String price; }
...
String json = "{\"price\":1234.5}";
Pojo p = new Gson().fromJson(json, Pojo.class);
System.out.println(p.price);
produces:
1234.5
When you want to access/get price as a double , convert it appropriately in the getter.

Storing different parameterized types in the same generic arraylist in Java

I like to create a class representation of database tables in java.
A column is designed as a generic class so that it can handle all different datatypes table columns can possible have.
public class TableColumn<T> {
...
}
A table has 0 ... n TableColumns, so my table class does look like that:
public class Table {
protected ArrayList<TableColumn<T>> columns =
new ArrayList<TableColumn<T>>();
...
}
The idea is to add columns in the following way.
Table t = new Table();
t.addColumn(String.class);
t.addColumn(Integer.class);
t.addColumn(Date.class);
t.addColumn(String.class);
And then i can manipulate data in the following way:
String a = t.Cols(2).Row(3);
t.Col(2).Row(3) = "b";
But i am loosing type safty with my current way of achiving that ... My problem is how to implement columns because of the different data types columns potential can get.
Does someone has a clue?
Why not just create a different object for each table you have? Something like:
Class Players with fields:
String name;
int points;
int number;
Class Stadium with fields:
String location;
Date dateBuilt;
Class Team with fields:
String name;
ArrayList<Players> roster;
Then you could just hold all the values in a list or arraylist and have them separated by the database tables and not have to guess at which table you are in. You'd have to hold onto more objects, but you would be able to know more what you're dealing with.
If there is a limited amount of Type combinations you could use interfaces to be those combinations. This would allow you to be able to store the column in the same way, and you wouldn't need any special casting.
t.addColumn(MyInterface.class);
Another method, which would still wouldn't be quite as clean as what you want but is kind of unavoidable, is to use a new Class that allows you to take the burden of some of the casting and type checking away.
Example:
public static class MyWrapper{
Class<?>[] validPossibleClasses;
Object o;
public MyWrapper(Class<?> ...classes){
this.validPossibleClasses = classes;
}
public boolean validateClass(Class<?> clazz){
for (Class<?> c : validPossibleClasses){
if (!c.isAssignableFrom(clazz))
return false;
}
return true;
}
public void set(Object o) throws Exception{
if (!validateClass(o.getClass()))
throw new Exception("Bad Cast");
this.o = o;
}
public String getString(){
return (String) o;
}
public Integer getInt(){
return (Integer) o;
}
...
// more specific getters
}
The usage would be like this
String a = t.Cols(2).Row(3).getString();
t.Col(2).Row(3).set("b");

Categories

Resources