title: Using Jackson to dynamically serialize an entity as either its ID or its full representation at runtime
body: <p>We're developing a RESTful API using Java EE 7 (RESTEasy / Hibernate / Jackson).</p>
  
  <p>We want the API to serialize all child entities using their IDs, by default. We're doing this mostly to maintain consistency with our deserialization strategy, where we insist on receiving an ID.</p>
  
  <p>However, we also want our users to be able to choose to get an expanded view of any of our child entities, either through a custom endpoint or a query parameter (undecided). For example:</p>
  
  <pre><code>// http://localhost:8080/rest/operator/1
  // =====================================
  {
      "operatorId": 1,
      "organization": 34,
      "endUser": 23
  }
  
  // http://localhost:8080/rest/operator/1?expand=organization
  // =====================================
  {
      "operatorId": 1,
      "organization": {
          "organizationId": 34,
          "organizationName": "name"
      },
      "endUser": 23
  }
  
  // http://localhost:8080/rest/operator/1?expand=enduser
  // =====================================
  {
      "operatorId": 1,
      "organization": 34,
      "endUser": {
          "endUserId": 23,
          "endUserName": "other name"
      }
  }
  
  // http://localhost:8080/rest/operator/1?expand=organization,enduser
  // =====================================
  {
      "operatorId": 1,
      "organization": {
          "organizationId": 34,
          "organizationName": "name"
      },
      "endUser": {
          "endUserId": 23,
          "endUserName": "other name"
      }
  }
  </code></pre>
  
  <p>Is there a way to dynamically change the behavior of Jackson to determine whether a specified AbstractEntity field is serialized in full form or as its ID? How might it be done?</p>
  
  <hr>
  
  <p><strong>Additional Info</strong></p>
  
  <p>We know of a few ways to serialize our child entities using their IDs, including:</p>
  
  <pre><code>public class Operator extends AbstractEntity {
      ...
      @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="organizationId")
      @JsonIdentityReference(alwaysAsId=true)
      public getOrganization() { ... }
      ...
  }
  </code></pre>
  
  <p>and</p>
  
  <pre><code>public class Operator extends AbstractEntity {
      ...
      @JsonSerialize(using=AbstractEntityIdSerializer.class)
      public getOrganization() { ... }
      ...
  }
  </code></pre>
  
  <p>where AbstractEntityIdSerializer serializes the entity using its ID.</p>
  
  <p>The problem is that we don't know of a way for the user to override that default behavior and revert to standard Jackson object serialization. Ideally they'd also be able to choose which child properties to serialize in full form.</p>
  
  <p>It would be awesome to dynamically toggle the alwaysAsId argument of @JsonIdentityReference for any property at runtime, if that's possible, or make the equivalent change to ObjectMapper/ObjectWriter.</p>
  
  <hr>
  
  <p><strong>Update: Working(?) Solution</strong>
  We haven't had a chance to fully test this yet, but I've been working on a solution that leverages overriding Jackson's AnnotationIntrospector class. It seems to be working as intended.</p>
  
  <pre><code>public class CustomAnnotationIntrospector extends JacksonAnnotationIntrospector {
      private final Set&lt;String&gt; expandFieldNames_;
  
      public CustomAnnotationIntrospector(Set&lt;String&gt; expandFieldNames) {
          expandFieldNames_ = expandFieldNames;
      }
  
      @Override
      public ObjectIdInfo findObjectReferenceInfo(Annotated ann, ObjectIdInfo objectIdInfo) {
          JsonIdentityReference ref = _findAnnotation(ann, JsonIdentityReference.class);
          if (ref != null) {
  
              for (String expandFieldName : expandFieldNames_) {
                  String expandFieldGetterName = "get" + expandFieldName;
                  String propertyName = ann.getName();
  
                  boolean fieldNameMatches = expandFieldName.equalsIgnoreCase(propertyName);
                  boolean fieldGetterNameMatches = expandFieldGetterName.equalsIgnoreCase(propertyName);
  
                  if (fieldNameMatches || fieldGetterNameMatches) {
                      return objectIdInfo.withAlwaysAsId(false);
                  }
              }
  
              objectIdInfo = objectIdInfo.withAlwaysAsId(ref.alwaysAsId());
          }
  
          return objectIdInfo;
      }
  }
  </code></pre>
  
  <p>At serialization time, we copy our ObjectMapper (so the AnnotationIntrospector runs again) and apply CustomAnnotationIntrospector as follows:</p>
  
  <pre><code>@Context
  private HttpRequest httpRequest_;
  
  @Override
  writeTo(...) {
      // Get our application's ObjectMapper.
      ContextResolver&lt;ObjectMapper&gt; objectMapperResolver = provider_.getContextResolver(ObjectMapper.class,
                                                                                        MediaType.WILDCARD_TYPE);
      ObjectMapper objectMapper = objectMapperResolver.getContext(Object.class);
  
      // Get Set of fields to be expanded (pre-parsed).
      Set&lt;String&gt; fieldNames = (Set&lt;String&gt;)httpRequest_.getAttribute("ExpandFields");
  
      if (!fieldNames.isEmpty()) {
          // Pass expand fields to AnnotationIntrospector.
          AnnotationIntrospector expansionAnnotationIntrospector = new CustomAnnotationIntrospector(fieldNames);
  
          // Replace ObjectMapper with copy of ObjectMapper and apply custom AnnotationIntrospector.
          objectMapper = objectMapper.copy();
          objectMapper.setAnnotationIntrospector(expansionAnnotationIntrospector);
      }
  
      ObjectWriter objectWriter = objectMapper.writer();
      objectWriter.writeValue(...);
  }
  </code></pre>
  
  <p>Any glaring flaws in this approach? It seems relatively straightforward and is fully dynamic.</p>