Skip to content

Instantly share code, notes, and snippets.

@benjaminaaron
Last active June 17, 2016 09:10
Show Gist options
  • Save benjaminaaron/acb5a92d43667e20b54bf2ef7a733cb7 to your computer and use it in GitHub Desktop.
Save benjaminaaron/acb5a92d43667e20b54bf2ef7a733cb7 to your computer and use it in GitHub Desktop.
Jackson: strict parsing of json-string into Map<ModelType, Attributes>
public abstract class Attributes {}
public class AttributesExample1 extends Attributes {
private String str = "foo";
private double dbl = 0.8;
private boolean bool = false;
private AttributesExample2 attributesExample2 = new AttributesExample2();
public String toString() {
return str + ", " + dbl + ", " + bool + ", attributesExample2: " + attributesExample2;
}
}
public class AttributesExample2 extends Attributes {
private int inti = 5;
private boolean booli = false;
public String toString() {
return inti + ", " + booli;
}
}
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.io.IOException;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.ACCEPT_FLOAT_AS_INT, false); // otherwise 4.7 will automatically be casted to 4 for integers, with this it throws an error
mapper.enable(JsonParser.Feature.STRICT_DUPLICATE_DETECTION); // forbids duplicate keys
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); // otherwise private fields won't be usable
mapper.registerModule(new SimpleModule().addDeserializer(boolean.class, new JsonDeserializer<Boolean>() { // make boolean parsing more strict, otherwise integers are accepted with 0=false and all other integers=true
@Override
public Boolean deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
if (!jsonParser.getCurrentToken().isBoolean())
throw new JsonParseException(jsonParser, "Can't parse \"" + jsonParser.getValueAsString() + "\" as boolean");
return jsonParser.getValueAsBoolean();
}
}));
String json = "{" +
"\"MODEL1\": {" +
"\"str\": \"hi\", " +
"\"dbl\": 2.0, " +
"\"bool\": true, " +
"\"attributesExample2\": {" +
"\"inti\": 4, " +
"\"booli\": true" +
"}}," +
"\"MODEL2\": {" +
"\"inti\": 2, " +
"\"booli\": false" +
"}}";
Map<ModelType, Attributes> modelsMap = new HashMap<>();
Iterator<Map.Entry<String, JsonNode>> nodeIterator = mapper.readTree(json).fields(); // via stackoverflow.com/a/18342245
while (nodeIterator.hasNext()) {
Map.Entry<String, JsonNode> entry = nodeIterator.next();
ModelType modelType = ModelType.valueOf(entry.getKey());
Attributes attributes = (Attributes) mapper.treeToValue(entry.getValue(), modelType.getAttributesClass()); // via stackoverflow.com/a/28714523
modelsMap.put(modelType, attributes);
}
System.out.println(modelsMap); // prints --> {MODEL2=2, false, MODEL1=hi, 2.0, true, attributesExample2: 4, true}
// TODO syntactic checks are done, now check modelsMap for logical soundness (order & valid mix?) before passing it to the simulator...
//usage example:
AttributesExample1 ae1 = (AttributesExample1) modelsMap.get(ModelType.MODEL1);
}
}
public enum ModelType {
MODEL1(AttributesExample1.class),
MODEL2(AttributesExample2.class);
private Class<? extends Attributes> attributes;
private ModelType(Class<? extends Attributes> attributesClass) {
attributes = attributesClass;
}
public Class<? extends Attributes> getAttributesClass() {
return attributes;
}
}
@schoettl
Copy link

ModelType Klasse würd ich anders machen. Bei dieser Version ist das Problem, dass man ein Model hinzufügen kann, aber es im static initializer vergessen könnte.

Man sollte das besser so machen:

public enum ModelType {
    MODEL1(AttributesModel1.class),
    MODEL2(AttributesModel2.class);

    private Class<? extends Attributes> attributes;

    private ModelType(Class<? extends Attributes> attributesClass) {
        attributes = attributesClass;
    }

    public Class<? extends Attributes> getAttributesClass() {
        return attributes;
    }
}

Außerdem für mehr Typsicherheit und um Warnungen keine Compiler-Warnungen zu haben, Class parametrisieren. Entweder Class<?> oder Class<? extends Attributes>. Aber ohne <> gibt's glaub ich eine "raw type warning" oder so.

@benjaminaaron
Copy link
Author

Danke, sieht besser aus, hab's übernommen 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment