How to deserialize from json tree? - java

Suppose I have json tree already read.
Is it possible to deserialize from it (without converting back to string)?
public class TryDeserializeNode {
public static class MyClass {
private int x = 11, y = 12;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
MyClass myClass = new MyClass();
String string = mapper.writeValueAsString(myClass);
JsonNode tree = mapper.readTree(string);
// how to deserialize from tree directly?
// MyClass myclass2 = mapper.readValue(tree.toString(), MyClass.class);
MyClass myclass2 = mapper.readValue(tree, MyClass.class);
}
}

You could simply use treeToValue:
MyClass myclass2 = mapper.treeToValue(tree, MyClass.class);
where mapper is your Jackson mapper and tree is your JsonNode.

Related

JAXB JavaTypeAdapter Not Working

I currently have the class State which contains a boolean and a DoublePair (my own class). The DoublePair class has two instance variables: x and y. When I try and marshal the file, I would have the entry <XY/> with no other information. After some googling, I was told to use an XmlJavaTypeAdapter but once I Unmarshall my file, I get 0.0 as opposed to the expected 1.0. (ultimately I'm trying to use the HueSDK and am using this as a model to debug. That is why I have a custom DoublePair class (the original models the one for the API)
My XML root element class:
#XmlRootElement
public class XMLManager {
private State state;
public XMLManager() {
}
public XMLManager(State state) {
this.state = state;
}
#XmlElement
public void setState(State state) {
this.state = state;
}
public State getState() {
return state;
}
public static void writeFile(XMLManager manager) {
File f = new File("src/data/dat.xml");
try {
JAXBContext context = JAXBContext.newInstance(XMLManager.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(manager, f);
} catch (JAXBException e) {
e.printStackTrace();
}
}
public static XMLManager readFile() {
File f = new File("src/data/dat.xml");
try {
JAXBContext context = JAXBContext.newInstance(XMLManager.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
return (XMLManager) unmarshaller.unmarshal(f);
} catch (JAXBException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
File xml = new File("src/data/dat.xml");
try {
if(!xml.createNewFile()) {
}
} catch (IOException e) {
e.printStackTrace();
}
XMLManager m = new XMLManager();
State s = new State();
s.setIsOn(true);
DoublePair dp = new DoublePair(1, 1);
s.setXY(dp);
m.setState(s);
XMLManager.writeFile(m);
m = XMLManager.readFile();
System.out.println(Arrays.toString(m.getState().getXY().getValue())); // returns [0.0, 0.0]
}
}
My State class:
public class State {
private boolean isOn;
#XmlJavaTypeAdapter(MyDoublePairAdapter.class)
private DoublePair xy;
public State() {
}
public State(boolean isOn, DoublePair xy) {
this.isOn = isOn;
this.xy = xy;
}
public void setIsOn(boolean isOn) {
this.isOn = isOn;
}
public boolean getIsOn() {
return isOn;
}
public void setXY(DoublePair xy) {
this.xy = xy;
}
public DoublePair getXY() {
return xy;
}
}
My DoublePair class:
public class DoublePair {
private double x;
private double y;
public DoublePair() {
}
public DoublePair(double x, double y) {
this.x = x;
this.y = y;
}
public double[] getValue() {
return new double[] {x, y};
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public void setValues(double[] values) {
x = values[0];
y = values[1];
}
public void setValue(double x, double y) {
this.x = x;
this.y = y;
}
}
My Custom DoublePair class (simplified version)
public class MyDoublePair {
private double x,y;
public MyDoublePair() {
this(0, 0);
}
public MyDoublePair(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public void setX(double x) {
this.x = x;
}
public void setY(double y) {
this.y = y;
}
}
My Adapter class:
public class MyDoublePairAdapter extends XmlAdapter<MyDoublePair, DoublePair> {
#Override
public MyDoublePair marshal(DoublePair val) throws Exception {
System.out.println("Marshal: " + val.getX()); // returns 1
return new MyDoublePair(val.getX(), val.getY());
}
#Override
public DoublePair unmarshal(MyDoublePair val) throws Exception {
System.out.println("Unmarshal: " + val.getX()); // returns 1
return new DoublePair(val.getX(), val.getY());
}
}
Here is the output of my XML file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xmlManager>
<state>
<xy>
<x>1.0</x>
<y>1.0</y>
</xy>
<isOn>true</isOn>
<XY/>
</state>
</xmlManager>
Any help would be greatly appreciated. I only resort to asking questions online when I am completely stumped.
I had a similar issue in the past.
In your case you just need XMLManager, State and DoublePair classes only. All you need to do is annotate DoublePair variable inside State as
#XmlElement
private DoublePair xy;
and, annotate x and y inside DoublePair class as:
#XmlElement
private double x;
#XmlElement
private double y;
This should work.

How to use Map<Object, Double> in Endpoints Framework with Java?

I have a class GChemin which contains a Map<GR2, Double> attribute. GR2 contains four double attributes. When generating the client-libs, the map become an JsonMap. My question is how to deal with this ?
I tried to populate the JsonMap with Gson formated JSON GR2 as key, but Endpoints can't handle it. I got this error :
com.fasterxml.jackson.databind.JsonMappingException: Can not find a (Map) Key deserializer for type [simple type, class GR2]
GR2 class :
public class GR2 implements Serializable
{
public GR2(double x, double y)
{
this.x = x;
this.y = y;
}
public GR2()
{
}
#Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("GR2 [x=");
builder.append(this.x);
builder.append(", y=");
builder.append(this.y);
builder.append("]");
return builder.toString();
}
public double getX()
{
return this.x;
}
public double getY()
{
return this.y;
}
public void setX(double x)
{
this.x = x;
}
public void setY(double y)
{
this.y = y;
}
private double x;
private double y;
}
Converting Map to JsonMap ( toG() method simply create a GR2 object (serializable) from a R2) :
public static JsonMap toJsonMap(Map<R2, Double> map)
{
JsonMap jsonMap = new JsonMap();
map.entrySet().forEach(entry -> jsonMap.put(toJSON(
toG(entry.getKey())),
entry.getValue()));
return jsonMap;
}
toJson() method :
public static String toJSON(GR2 gr2)
{
return new Gson().toJson(gr2);
}
Api method (gChemin contains the attribute map)
#ApiMethod(name = "save", httpMethod = HttpMethod.POST)
public GBoolean save(GChemin gChemin)
{
// Work with gChemin
}

builder pattern json deserialize

I have a problem. I just used the example of jackson json for deserializing builder pattern but I always get an empty json.
I use jackson-databind version 2.8.4
Am I missing something?
So my code is as follows:
The Value class
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
#JsonDeserialize(builder=ValueBuilder.class)
public class Value {
private final int x, y;
protected Value(int x, int y) {
this.x = x;
this.y = y;
}
}
The ValueBuilder Class
import com.fasterxml.jackson.annotation.JsonCreator;
//#JsonPOJOBuilder(buildMethodName = "build", withPrefix = "with")
public class ValueBuilder {
private int x;
private int y;
// can use #JsonCreator to use non-default ctor, inject values etc
public ValueBuilder() { }
// if name is "withXxx", works as is: otherwise use #JsonProperty("x") or #JsonSetter("x")!
public ValueBuilder withX(int x) {
this.x = x;
return this; // or, construct new instance, return that
}
public ValueBuilder withY(int y) {
this.y = y;
return this;
}
#JsonCreator
public Value build() {
return new Value(x, y);
}
}
The Start Class
public class Start {
public static void main(String[] args) throws IOException {
Value newValue = new ValueBuilder().withX(2).withY(4).build();
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(newValue);
System.out.println(jsonString);
}
}
You're only missing accessible getters for x and y in your Value class - the ObjectMapper requires access to those in order to serialize.
Add the following to your Value class definition:
public int getX() {
return x;
}
public int getY() {
return y;
}
No need for additional annotations in this context.
Your JSON will print out like:
{"x":2,"y":4}
You could also make the fields public to reach the same result, but that would defile proper encapsulation.

Map with custom key in Groovy vs Java

I want to use a map in Groovy where the keys will be instances of an unmutable class.
This is something I do often in Java and it works fine, like in this example class:
public class TestMap {
static final class Point {
final int x; final int y;
public Point(int x, int y) {this.x = x;this.y = y;}
}
public static void main(String[] args) {
Map<Point, String> map = new HashMap<>();
final Point origin = new Point(0, 0);
map.put(origin, "hello world !" );
if(!map.containsKey(origin))
throw new RuntimeException("can't find key origin in the map");
if(!map.containsKey(new Point(0,0))) {
throw new RuntimeException("can't find new key(0,0) in the map");
}
}
}
But when I try to achieve the same thing with Groovy, it doesn't work.
Why ?
Here is a sample non working example in Groovy:
class Point {
final int x; final int y
Point(int x, int y) { this.x = x; this.y = y }
public String toString() { return "{x=$x, y=$y}" }
}
def origin = new Point(0, 0)
def map = [(origin): "hello"]
map[(new Point(1,1))] = "world"
map.put(new Point(2,2), "!")
assert map.containsKey(origin) // this works: when it's the same ref
assert map.containsKey(new Point(0,0))
assert map.containsKey(new Point(1,1))
assert map.containsKey(new Point(2,2))
assert !map.containsKey(new Point(3,3))
You need to have an equals and hashCode method on your Point class so that the instances can be found as keys in the HashMap
You can do this quickly by adding an annotation in Groovy:
import groovy.transform.*
#EqualsAndHashCode
class Point {
final int x; final int y
Point(int x, int y) { this.x = x; this.y = y }
public String toString() { return "{x=$x, y=$y}" }
}

Generic behaviour

(sorry for the pun)
Say one wants to define a generic builder, like this:
public abstract class GenericBuilder<T extends Product> {
int x;
int y;
<K extends GenericBuilder<T>> K setX(int x) {
this.x = x;
return (K)this;
}
<K extends GenericBuilder<T>> K setY(int y) {
this.y = y;
return (K) this;
}
abstract T build();
}
abstract class Product {
int x;
int y;
}
class ConcreteProduct extends Product {
int z;
}
class ConcreteBuilder extends GenericBuilder<ConcreteProduct>{
int z;
<K extends GenericBuilder<ConcreteProduct>> K setZ(int z) {
this.z = z;
return (K) this;
}
#Override
ConcreteProduct build() {
ConcreteProduct cp = new ConcreteProduct();
cp.x = x;
cp.y = y;
cp.z = z;
return cp;
}
public static void main(String[] args) {
new ConcreteBuilder().setX(1).setY(2).setZ(3);
}
}
When calling ConcreteBuilder.setZ(), it fails during compilation.
Why is that? Is it due erasure? Or the generics, say, don't carry information about its generic parameters?
EDIT:
Any ideas how to avoid using second generic parameter in:
public class ConcreteBuilder extends GenericBuilder<ConcreteProduct, ConcreteBuilder>
i.e. <..., ConcreteBuilder>, which seems to be a little clumsy? I guess it's not possible. Are there other languages (C# maybe?) which allow to do that?
Break your code this way and you will understand that your class GenericBuilder<ConcreteProduct> doesn't have any setZ() method defined.
GenericBuilder<ConcreteProduct> setY = new ConcreteBuilder().setX(1).setY(2);
setY.setZ(3);
In your GenericBuilder your functions return a GenericBuilder when you don't specify the type argument of the function. In your main function the call to setX returns a GenericBuilder and you loose the information that you are actually using a ConcreteBuilder. To succesfully make the calls, you have to specify the generic parameters for the setters:
new ConcreteBuilder().<ConcreteBuilder>setX(1).<ConcreteBuilder>setY(2).setZ(3);
Alternative
You can add a second type parameter to GenericBuilder:
public abstract class GenericBuilder<T extends Product, K extends GenericBuilder<T, K>> {
int x;
int y;
K setX(int x) {
this.x = x;
return (K)this;
}
K setY(int y) {
this.y = y;
return (K) this;
}
abstract T build();
}
and change ConcreteBuilder to this:
public class ConcreteBuilder extends GenericBuilder<ConcreteProduct, ConcreteBuilder> {
int z;
ConcreteBuilder setZ(int z) {
this.z = z;
return this;
}
#Override
public ConcreteProduct build() {
ConcreteProduct cp = new ConcreteProduct();
cp.x = x;
cp.y = y;
cp.z = z;
return cp;
}
public static void main(String[] args) {
new ConcreteBuilder().setX(1).setY(2).setZ(3);
}
}

Categories

Resources