Skip to content

Polymorphic JSON merge patch models may cause properties to be included in update when deserializing #2769

@alzimmermsft

Description

@alzimmermsft

While regenerating an SDK to use stream-style serialization I found that polymorphic properties aren't being handled correctly during deserialization if they support JSON merge patch.

The current design of JSON stream-style deserialization uses the model setter if the property is defined by a super type. JSON merge patch uses the setter to determine if a property should be included in serialization when the model is used in a patch operation. These together result in the parent properties always being included in serialization, and possibly in an incorrect way if the value is null and something else sets it in-between deserialization and using the model in a patch operation.

Code sample

public class Parent {
  private String property;

  public Parent setProperty(String property) {
    this.property = property;
    // Track property for patch serialization
    return this;
  }
}

public class Child extends Parent {
  public Child setProperty(String property) {
    super.setProperty(property);
    // Track property for patch serialization
    return this;
  }

  public static Child fromJson(JsonReader jsonReader) throws IOException {
    // logic
    if ("property".equals(fieldName) {
      childInstance.setProperty(jsonReader.getString());
    }
    // logic
  }
}

Solutions

At this time there are two solutions I can think of:

  1. The models within the polymorphic hierarchy have internal setters that don't track the property's inclusion into patch serialization.
  2. The parent model defines the property as package-private and all children models change deserialization to set the property directly.

My preference is for option #2 as option #1 would require each child model within the hierarchy to add an internal setter as well. Additionally, option #2 can be used universally, even when the model isn't used in JSON merge patch operations and would allow for the removal of calls like:

public String getProperty() {
  return super.getProperty();
}

public Type setProperty(String property) {
  super.setProperty(property);
  return this;
}

and allow for direct access to the field:

public String getProperty() {
  return property;
}

public Type setProperty(String property) {
  this.property = property;
  return this;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions