@Override
  public Tuple convert(String source) {
    TupleBuilder builder = TupleBuilder.tuple();
    try {

      JsonNode root = mapper.readTree(source);
      for (Iterator<Entry<String, JsonNode>> it = root.fields(); it.hasNext(); ) {
        Entry<String, JsonNode> entry = it.next();
        String name = entry.getKey();
        JsonNode node = entry.getValue();
        if (node.isObject()) {
          // tuple
          builder.addEntry(name, convert(node.toString()));
        } else if (node.isArray()) {
          builder.addEntry(name, nodeToList(node));
        } else if (node.isNull()) {
          builder.addEntry(name, null);
        } else if (node.isBoolean()) {
          builder.addEntry(name, node.booleanValue());
        } else if (node.isNumber()) {
          builder.addEntry(name, node.numberValue());
        } else {
          builder.addEntry(name, mapper.treeToValue(node, Object.class));
        }
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
    return builder.build();
  }
  @Override
  public Tuple getGroupTuple(Tuple keysTuple) {
    Tuples.asModifiable(joinedTuple);

    for (int i = 0; i < collections.length; i++)
      joinedTuplesArray[i] = collections[i].isEmpty() ? emptyTuple : keysTuple;

    joinedTuple = joinedBuilder.makeResult(joinedTuplesArray);

    return joinedTuple;
  }
  @Override
  public Tuple convert(String source) {
    TupleBuilder builder = TupleBuilder.tuple();
    try {

      JsonNode root = mapper.readTree(source);
      for (Iterator<Entry<String, JsonNode>> it = root.fields(); it.hasNext(); ) {
        Entry<String, JsonNode> entry = it.next();
        String name = entry.getKey();
        JsonNode node = entry.getValue();
        if (node.isObject()) {
          // tuple
          builder.addEntry(name, convert(node.toString()));
        } else if (node.isArray()) {
          builder.addEntry(name, nodeToList(node));
        } else {
          if (name.equals("id")) {
            // TODO how should this be handled?
          } else if (name.equals("timestamp")) {
            // TODO how should this be handled?
          } else {
            builder.addEntry(name, node.asText());
          }
        }
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
    return builder.build();
  }