docs: public static Document read(@NonNull Resource resource) throws IOException {
        Type mapType = new TypeToken<Map<String, Object>>() {
        }.getType();
        Gson gson = new Gson();
        //Load the document to a map
        Map<String, Object> json = gson.fromJson(resource.readToString(), mapType);
  
        //Create the initial document using raw content
        Document doc = DocumentFactory.getInstance().createRaw(json.getOrDefault("id", Val.NULL).toString(),
                                                               json.getOrDefault("content", "").toString());
  
        //Add document attributes
        if (json.containsKey("attributes")) {
           Map<String, Object> attributes = Cast.as(json.get("attributes"));
           attributes.forEach((k, av) -> {
              AttributeType attrType = Types.attribute(k);
              Object val = attrType.getValueType().decode(av);
              if (val != null) {
                 doc.put(attrType, val);
              }
           });
        }
  
        //Set completed annotations
        if (json.containsKey("completed")) {
           Map<String, Object> completed = Cast.as(json.get("completed"));
           completed
              .forEach((k, av) -> {
                 AnnotatableType type = Types.from(k);
                 doc.getAnnotationSet().setIsCompleted(type, true, av.toString());
              });
        }
  
        //Create the annotations
        AtomicLong maxAnnotationId = new AtomicLong(-1);
        if (json.containsKey("annotations")) {
           List<Object> annotations = Cast.as(json.get("annotations"));
           annotations
              .forEach(v -> {
                 Map<String, Object> vv = Cast.as(v);
  
                 //Create new annotation
                 Annotation annotation = doc.annotationBuilder()
                                            .type(AnnotationType.create(vv.get("type").toString()))
                                            .start(((Number) vv.get("start")).intValue())
                                            .end(((Number) vv.get("end")).intValue())
                                            .createAttached();
  
                 //Read in and set id and update max id value
                 annotation.setId(((Number) vv.get("id")).longValue());
                 maxAnnotationId.getAndUpdate(old -> old < annotation.getId() ? annotation.getId() : old);
  
                 //Read in the attributes (map of types and values)
                 Cast.<Map<String, Object>>as(vv.getOrDefault("attributes", Collections.emptyMap())).forEach(
                    (k, av) -> {
                       AttributeType attrType = Types.attribute(k);
                       Object val = attrType.getValueType().decode(av);
                       if (val != null) {
                          annotation.put(attrType, val);
                       }
                    });
  
                 if (vv.containsKey("relations")) {
                    //Read in the relations (a list of maps containing type, value, and target)
                    Cast.<List<Object>>as(vv.get("relations"))
                       .stream()
                       .map(Cast::<Map<String, Object>>as)
                       .forEach(rel -> annotation.add(new Relation(RelationType.create(rel.get("type").toString()),
                                                                   rel.get("value").toString(),
                                                                   ((Number) rel.get("target")).longValue())));
                 }
              });
  
           //Set the next annotation id
           doc.idGenerator.set(maxAnnotationId.get() + 1);
        }
  
        return doc;
     }