void xml_formatter::write_changeset(const changeset_info &elem,
                                    const tags_t &tags,
                                    bool include_comments,
                                    const comments_t &comments,
                                    const pt::ptime &now) {
  writer->start("changeset");

  writer->attribute("id", elem.id);
  writer->attribute("created_at", elem.created_at);
  const bool is_open = elem.is_open_at(now);
  if (!is_open) {
    writer->attribute("closed_at", elem.closed_at);
  }
  writer->attribute("open", is_open);

  if (bool(elem.display_name) && bool(elem.uid)) {
    writer->attribute("user", elem.display_name.get());
    writer->attribute("uid", elem.uid.get());
  }

  if (elem.bounding_box) {
    writer->attribute("min_lat", elem.bounding_box->minlat);
    writer->attribute("min_lon", elem.bounding_box->minlon);
    writer->attribute("max_lat", elem.bounding_box->maxlat);
    writer->attribute("max_lon", elem.bounding_box->maxlon);
  }

  writer->attribute("comments_count", elem.comments_count);
  writer->attribute("changes_count", elem.num_changes);

  write_tags(tags);

  if (include_comments) {
    writer->start("discussion");
    for (comments_t::const_iterator itr = comments.begin();
         itr != comments.end(); ++itr) {
      writer->start("comment");
      writer->attribute("date", itr->created_at);
      writer->attribute("uid", itr->author_id);
      writer->attribute("user", itr->author_display_name);
      writer->start("text");
      writer->text(itr->body);
      writer->end();
      writer->end();
    }
    writer->end();
  }

  writer->end();
}
void json_formatter::write_changeset(const changeset_info &elem,
                                     const tags_t &tags,
                                     bool include_comments,
                                     const comments_t &comments,
                                     const std::chrono::system_clock::time_point &now) {

  writer->start_object();

  WRITE_KV("type", string, "changeset");

  writer->object_key("id");
  writer->entry_int(elem.id);

  writer->object_key("created_at");
  writer->entry_string(elem.created_at);

  const bool is_open = elem.is_open_at(now);
  if (!is_open) {
      writer->object_key("closed_at");
      writer->entry_string(elem.closed_at);
  }

  writer->object_key("open");
  writer->entry_bool(is_open);

  if (elem.display_name && bool(elem.uid)) {
    writer->object_key("user");
    writer->entry_string(elem.display_name.get());
    writer->object_key("uid");
    writer->entry_int(elem.uid.get());
  }

  if (elem.bounding_box) {
      writer->object_key("minlat");
      writer->entry_double(elem.bounding_box->minlat);
      writer->object_key("minlon");
      writer->entry_double(elem.bounding_box->minlon);
      writer->object_key("maxlat");
      writer->entry_double(elem.bounding_box->maxlat);
      writer->object_key("maxlon");
      writer->entry_double(elem.bounding_box->maxlon);
  }

  writer->object_key("comments_count");
  writer->entry_int(elem.comments_count);

  writer->object_key("changes_count");
  writer->entry_int(elem.num_changes);

  write_tags(tags);

  if (include_comments && !comments.empty()) {
      writer->object_key("discussion");
      writer->start_array();
      for (const auto & comment : comments) {
	  writer->start_object();
	  writer->object_key("date");
	  writer->entry_string(comment.created_at);
	  writer->object_key("uid");
	  writer->entry_int(comment.author_id);
	  writer->object_key("user");
	  writer->entry_string(comment.author_display_name);
	  writer->object_key("text");
	  writer->entry_string(comment.body);
	  writer->end_object();
      }
      writer->end_array();
  }

  writer->end_object();
}