4

I am trying to deserialize a object ref ($ref) using ObjectMapper.

 public class Foo {
    @JsonProperty("bar")
    private Bar bar;

    @JsonProperty("bar")
    public Bar getBar() {
        return bar;
    }

    @JsonProperty("bar")
    public void setBar(Bar bar) {
        this.bar = bar;
    }
}

test.json This is the json file I am trying to deserialize. Is this is the correct way to refer to a object/json reference?

{  
  "bar": {"$ref": "/bar.json"}
}

Deserializer.java

ObjectMapper objectMapper = new ObjectMapper();
//load class
URL url = Deserializer.class.getClassLoader().getResource("test.json");

//deserialize 
objectMapper.readValue(url, Foo.class);

the result creates a Foo pojo with additional property of "bar": ""$ref": "/bar.json"" rather than deserializing it. Do I need to implement the deserialize interface and manually deserialize the node?

atom
  • 157
  • 3
  • 10

2 Answers2

1

Traditionally in Comp Sc. this problem is solved using what is known as "Pointer Swizzling".

This means that If you have an Object A that contains a reference to B and you want to serialize this structure (and then deserialize it), you would need to "unswizzle" the pointer to B to a "name" (an identifier that uniquely identifies the instance B) , write it to disk. When deserializing, you would then take that name, find the instance that it points to (B) and "swizzle" the name back to a proper pointer to B.

Now, in Java pointers are called references but it's the same.

Here's an example to illustrate:

originalA = { "id":"id_a", "ref_to_b": originalB}
originalB = { "id":"id_b" }

Applying unswizzling:

readyForSerializationA = { "id":"id_a", "ref_to_b": "id_b"}
readyForSerializationB = { "id": "id_b" }

followed by writing to store/reading back from store.

Applying swizzling:

deserializedB = { "id":"id_b" }
deserializedA = { "id": "id_a", "ref_to_b": deserializedB}

One possible way to do it for your case,is to deserialize all objects first, put them into an HashMap and in a second pass, look up the ObjectReference(s) from the various ObjectID(s) that you have in your JSON (swizzling).

Some further reading: https://en.wikipedia.org/wiki/Pointer_swizzling

Edd
  • 1,350
  • 11
  • 14
  • thanks for sharing. I was reading jackson docs and don't see anything out of the box which supports pointer swizzling. Would the approach of manually going through the tree and calling objectMapper on the node "bar" with the respective class be wrong? – atom Mar 10 '17 at 09:15
  • 1
    My pleasure. I was also looking for that before answering and didn't see a built-in swizzling feature. Maybe someone will swoop in and let us both know if it exists. But you could try yourself. It shouldn't be too much work with the help of a HashMap. At the very least, it would be an interesting exercise and I argue that it can efficiently solve the problem as well. You can do it as you say but you would not preserve the "original" reference to bar. It may or may not be important for your use case. – Edd Mar 10 '17 at 09:22
0

You need to store {"$ref": "/bar.json"} this as a Map. That's the simplest way to store it.

Example:

public class Foo {
    @JsonProperty("bar")
    private Map<String, Bar> bar;

    @JsonProperty("bar")
    public Map<String, Bar> getBar() {
        return bar.get("$ref");
    }

    @JsonProperty("bar")
    public void setBar(Map<String, Bar> bar) {
        this.bar = bar;
    }
}

Only then it will get the value of $ref in Bar object. Otherwise, the data will be in incorrect format and Bar object will take the entire bar value into it.

cruck
  • 5
  • 2
  • thanks for the input. However the suggested approach will not work since it tries invokes Bar(String s) constructor with value '/bar.json'. Goal here is to deserialize the bar.json which is on the path ''/' – atom Mar 10 '17 at 09:14
  • 1
    Then it's better to deserialize test.json normally instead of loading it into a class. After that, u can get the contents of bar.json and desrialize by using a corresonding object. – cruck Mar 10 '17 at 09:23
  • What is the meaning of "deserialize normally instead of loading into a class"? Until now I'm convinced that "deserialization" means nothing else than creating objects from a (string) transfer format. Ok, instead of an object it could be a data struckture but that would be a redundant intermediate step because you could do text replace on the json string as well. – The incredible Jan Mar 22 '17 at 07:03
  • You can deserialize by using bracket tags. Whenever a bracket is encountered, you can go for next value present inside that token. I might be confusing you more now, well just look at jackson jsonParser class; you can find many useful stuff there. For example, jsonparser. getValueAsString() etc., – cruck Mar 23 '17 at 11:19