private static void writeAttributes(HasAttributes obj, JsonObject out) {
    if (obj.getAttributeKeys().size() > 0) {
      JsonObject properties = new JsonObject();

      for (String key : Sorters.sortSet(obj.getAttributeKeys())) {
        writeString(key, obj.getAttribute(key), properties);
      }

      out.add("properties", properties);
    }
  }
  JsonObject writeTextAnnotation(TextAnnotation ta, boolean doWriteTokenOffsets) {

    // get rid of the views that are empty
    Set<String> viewNames = new HashSet<>(ta.getAvailableViews());
    for (String vu : viewNames) {
      if (ta.getView(vu) == null) {
        logger.warn("View " + vu + " is null");
        ta.removeView(vu);
      }
    }

    JsonObject json = new JsonObject();

    writeString("corpusId", ta.getCorpusId(), json);
    writeString("id", ta.getId(), json);
    writeString("text", ta.getText(), json);
    writeStringArray("tokens", ta.getTokens(), json);
    if (doWriteTokenOffsets) writeTokenOffsets(TOKENOFFSETS, ta.getView(ViewNames.TOKENS), json);

    writeSentences(ta, json);

    JsonArray views = new JsonArray();
    for (String viewName : Sorters.sortSet(ta.getAvailableViews())) {
      if (viewName.equals(ViewNames.SENTENCE)) continue;

      JsonObject view = new JsonObject();

      writeString("viewName", viewName, view);
      views.add(view);

      JsonArray viewData = new JsonArray();
      List<View> topKViews = ta.getTopKViews(viewName);

      for (View topKView : topKViews) {
        JsonObject kView = new JsonObject();
        writeView(topKView, kView);
        viewData.add(kView);
      }

      view.add("viewData", viewData);
    }

    json.add("views", views);

    writeAttributes(ta, json);

    return json;
  }
 private static void writeLabelsToScores(Map<String, Double> obj, JsonObject out) {
   JsonObject labelScoreMap = new JsonObject();
   for (String key : Sorters.sortSet(obj.keySet())) writeDouble(key, obj.get(key), out);
   out.add(LABEL_SCORE_MAP, labelScoreMap);
 }