public static Trace deserialize(final JsonNode rootNode) throws IOException {
   final TraceRelationshipBuilder<Integer> traceBuilder = new TraceRelationshipBuilder<Integer>();
   try {
     parseTraces(rootNode, traceBuilder);
     parseRelationships(rootNode, traceBuilder);
     return traceBuilder.buildRoot();
   } catch (RuntimeException e) {
     throw new IOException(e);
   }
 }
  private static void parseRelationships(
      final JsonNode rootNode, final TraceRelationshipBuilder<Integer> builder) throws IOException {
    if (builder.isEmpty()) {
      throw new IOException("No traces in the document");
    }

    for (JsonNode node : getField(rootNode, JsonTraceCodec.RELATIONSHIPS)) {
      final String relationship = getTextField(node, JsonTraceCodec.RELATIONSHIP_RELATIONSHIP);
      final int from = getIntField(node, JsonTraceCodec.RELATIONSHIP_FROM);
      final int to = getIntField(node, JsonTraceCodec.RELATIONSHIP_TO);
      builder.addRelationship(relationship, from, to);
    }
  }
  private static void parseTraces(
      final JsonNode rootNode, final TraceRelationshipBuilder<Integer> builder) throws IOException {
    for (JsonNode traceNode : getField(rootNode, JsonTraceCodec.TRACES)) {
      final int traceId = getIntField(traceNode, JsonTraceCodec.TRACE_ID);
      final String name = getTextField(traceNode, JsonTraceCodec.TRACE_NAME);
      final ResultType resultType =
          ResultType.valueOf(getTextField(traceNode, JsonTraceCodec.TRACE_RESULT_TYPE));
      final ShallowTraceBuilder shallowBuilder = new ShallowTraceBuilder(name, resultType);

      if (traceNode.get(JsonTraceCodec.TRACE_HIDDEN) != null)
        shallowBuilder.setHidden(getBooleanField(traceNode, JsonTraceCodec.TRACE_HIDDEN));

      if (traceNode.get(JsonTraceCodec.TRACE_SYSTEM_HIDDEN) != null)
        shallowBuilder.setSystemHidden(
            getBooleanField(traceNode, JsonTraceCodec.TRACE_SYSTEM_HIDDEN));

      if (traceNode.get(JsonTraceCodec.TRACE_VALUE) != null)
        shallowBuilder.setValue(getTextField(traceNode, JsonTraceCodec.TRACE_VALUE));

      if (traceNode.get(JsonTraceCodec.TRACE_START_NANOS) != null)
        shallowBuilder.setStartNanos(getLongField(traceNode, JsonTraceCodec.TRACE_START_NANOS));

      if (traceNode.get(JsonTraceCodec.TRACE_END_NANOS) != null)
        shallowBuilder.setEndNanos(getLongField(traceNode, JsonTraceCodec.TRACE_END_NANOS));

      if (traceNode.get(JsonTraceCodec.TRACE_ATTRIBUTES) != null) {
        for (JsonNode node : getField(traceNode, JsonTraceCodec.TRACE_ATTRIBUTES)) {
          String key = getTextField(node, JsonTraceCodec.TRACE_ATTRIBUTE_KEY);
          String value = getTextField(node, JsonTraceCodec.TRACE_ATTRIBUTE_VALUE);
          shallowBuilder.addAttribute(key, value);
        }
      }

      builder.addTrace(traceId, shallowBuilder.build());
    }
  }