Jackson JSON Processor
  1. Jackson JSON Processor
  2. JACKSON-235

Handle bi-directional references using declarative method(s)

    Details

    • Type: New Feature New Feature
    • Status: Closed Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: 1.6
    • Component/s: None
    • Labels:
      None
    • Number of attachments :
      0

      Description

      One fairly common problem case is that of bi-directional linking between objects; most commonly with parent/child relationships.
      By default these can lead to infinite recursion (due to cyclic reference). But for simple parent/child style relationships, it seems possible to find a way to handle linkage gracefully.

      It might be possible to annotate one or both links in a way that allows suppression of serialization for one link (usually the "parent" link), and then recreating the linkage on deserialization. Currently this can only be done by modifying value classes to do this. This is fairly simple thing to do, but requires ability to modify code, and also adds serialization-specific code in otherwise regular POJOs. As such, annotation-based solution would be beneficial.

        Activity

        Hide
        Tatu Saloranta added a comment -

        Kyrill: just like @JsonIgnore for serialization, but not when deserializing: in latter case link is re-constructed from context.

        Michael: correct, there is the bug JACKSON-368 and matching unit test: these two features unfortunately do not play nice, and I don't yet know how to fix the issue. It seems rather tricky one, unfortunately. But it is not forgotten.
        So hope definitely is to support this, but it is not a simple bug to fix (unless I am overlooking something obvious). Make sure to vote for the bug if/when you have hit it, that makes it easier to find priorities for long(er) standing bugs.

        Show
        Tatu Saloranta added a comment - Kyrill: just like @JsonIgnore for serialization, but not when deserializing: in latter case link is re-constructed from context. Michael: correct, there is the bug JACKSON-368 and matching unit test: these two features unfortunately do not play nice, and I don't yet know how to fix the issue. It seems rather tricky one, unfortunately. But it is not forgotten. So hope definitely is to support this, but it is not a simple bug to fix (unless I am overlooking something obvious). Make sure to vote for the bug if/when you have hit it, that makes it easier to find priorities for long(er) standing bugs.
        Hide
        Thomas Christensen added a comment -

        It is mentioned in the above comment that JsonBackReference does not work for deserialisation and I can verify that this is the case. When doing a Parent-Child relation with JsonManagedReference and JsonBackReference, the parent on the child does not get set during deserialisation.
        Shouldn't this issue be reopened or is this bug covered in JACKSON-368?

        Show
        Thomas Christensen added a comment - It is mentioned in the above comment that JsonBackReference does not work for deserialisation and I can verify that this is the case. When doing a Parent-Child relation with JsonManagedReference and JsonBackReference, the parent on the child does not get set during deserialisation. Shouldn't this issue be reopened or is this bug covered in JACKSON-368 ?
        Hide
        Tatu Saloranta added a comment -

        Deserialization works just fine for other types (as far as I know, as per tests), but the specific case of types that use polymorphic type information (@JsonTypeInfo) does not work.
        That is, the problem is with combination of these two features; specifically that parent type handling must know field/method to use for both sides, for relinking, and this is not available at the point where it is currently done (since abstract type is resolved at a bit later step).

        But if you do have issues for other cases (possibly found another bug), please file a new Jira bug (or ask on the mailing list) so we can see what is happening.
        There are other things that could be causing issues; for example, many users get bitten by the fact that annotations are not shared between getters and setters; and so if annotations are only added on, say, getters (which are used for serialization) they will not currently affect deserialization unless setter also has it. This is not problem with fields, as same field is used for both operations.

        Show
        Tatu Saloranta added a comment - Deserialization works just fine for other types (as far as I know, as per tests), but the specific case of types that use polymorphic type information (@JsonTypeInfo) does not work. That is, the problem is with combination of these two features; specifically that parent type handling must know field/method to use for both sides, for relinking, and this is not available at the point where it is currently done (since abstract type is resolved at a bit later step). But if you do have issues for other cases (possibly found another bug), please file a new Jira bug (or ask on the mailing list) so we can see what is happening. There are other things that could be causing issues; for example, many users get bitten by the fact that annotations are not shared between getters and setters; and so if annotations are only added on, say, getters (which are used for serialization) they will not currently affect deserialization unless setter also has it. This is not problem with fields, as same field is used for both operations.
        Hide
        Thomas Christensen added a comment -

        It does seem to work both with field annotation and property annotation - however for property annotation you are required to annotate both get+set on both sides of the relation.
        F.ex: in a Parent-Child relation where parent can have many children the following annotations are required if deserialization is to work:

         
        public class Child {
            private String name;
            private Parent parent;
            public String getName() {
                return name;
            }
            public void setName(String name) {
                this.name = name;
            }
            @JsonBackReference
            public Parent getParent() {
                return parent;
            }
            @JsonBackReference
            public void setParent(Parent parent) {
                this.parent = parent;
            }
        }
        
        public class Parent {
            private String name;
            private List<Child> children = new ArrayList<Child>();
            public void addChild(Child child) {
                if (!getChildren().contains(child)) {
                    getChildren().add(child);
                    if (child.getParent() != null) {
                        child.getParent().getChildren().remove(child);
                    }
                    child.setParent(this);
                }
            }
            public String getName() {
                return name;
            }
            public void setName(String name) {
                this.name = name;
            }
            @JsonManagedReference
            public List<Child> getChildren() {
                return children;
            }
            @JsonManagedReference
            public void setChildren(List<Child> children) {
                this.children = children;
            }
        }
        

        If setChildren is either missing or not annotated child.getParent will be null when deserialized from JSON. Serialization works without it. Design-wise addChild could make setChildren obsolete, but for this to work it is required to be exposed.

        Show
        Thomas Christensen added a comment - It does seem to work both with field annotation and property annotation - however for property annotation you are required to annotate both get+set on both sides of the relation. F.ex: in a Parent-Child relation where parent can have many children the following annotations are required if deserialization is to work: public class Child { private String name; private Parent parent; public String getName() { return name; } public void setName( String name) { this .name = name; } @JsonBackReference public Parent getParent() { return parent; } @JsonBackReference public void setParent(Parent parent) { this .parent = parent; } } public class Parent { private String name; private List<Child> children = new ArrayList<Child>(); public void addChild(Child child) { if (!getChildren().contains(child)) { getChildren().add(child); if (child.getParent() != null ) { child.getParent().getChildren().remove(child); } child.setParent( this ); } } public String getName() { return name; } public void setName( String name) { this .name = name; } @JsonManagedReference public List<Child> getChildren() { return children; } @JsonManagedReference public void setChildren(List<Child> children) { this .children = children; } } If setChildren is either missing or not annotated child.getParent will be null when deserialized from JSON. Serialization works without it. Design-wise addChild could make setChildren obsolete, but for this to work it is required to be exposed.
        Hide
        Tatu Saloranta added a comment -

        Correct: setter/getter pairing is a known and long-standing sub-standard feature of annotation handling.
        So solution to that particular problem has to be done for the whole annotation processing framework, and will require extensive changes to some internal (or semi-public, in case of AnnotationIntrospector) abstractions. This is the main reason this has not been changed yet.

        So, this is not a problem specific to this feature, unfortunately.

        Show
        Tatu Saloranta added a comment - Correct: setter/getter pairing is a known and long-standing sub-standard feature of annotation handling. So solution to that particular problem has to be done for the whole annotation processing framework, and will require extensive changes to some internal (or semi-public, in case of AnnotationIntrospector) abstractions. This is the main reason this has not been changed yet. So, this is not a problem specific to this feature, unfortunately.

          People

          • Assignee:
            Tatu Saloranta
            Reporter:
            Tatu Saloranta
          • Votes:
            1 Vote for this issue
            Watchers:
            7 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: