29 explicit JsonWriter(std::ostream& os) : os_(os), linesep_(
"\n ") {}
50 void change_indent(
int n) { linesep_.resize(linesep_.size() + n,
' '); }
53 std::string get_tag_category(
const std::string& tag)
const {
56 size_t pos = tag.find(
'.');
57 if (pos == std::string::npos)
59 return tag.substr(0, pos + 1);
62 std::string get_loop_category(
const Loop& loop)
const {
63 if (loop.tags.empty())
65 std::string
cat = get_tag_category(loop.tags[0]);
66 for (
size_t i = 1; i < loop.tags.size(); ++i)
73 static void escape(std::ostream& os,
const std::string& s,
size_t pos,
75 static const char* h =
"0123456789abcdef";
76 const char* p = s.data() + pos;
78 const char*
const e = s.data() + s.size();
80 const unsigned char c = *p;
85 }
else if (c ==
'"') {
93 case '\b': os <<
"\\b";
break;
94 case '\f': os <<
"\\f";
break;
95 case '\n': os <<
"\\n";
break;
96 case '\r': os <<
"\\r";
break;
97 case '\t': os <<
"\\t";
break;
98 default: os <<
"\\u00" << h[(c & 0xf0) >> 4] << h[c & 0x0f];
100 }
else if (
to_lower && c >=
'A' && c <=
'Z') {
104 }
else if (c == 127) {
115 void write_string(
const std::string& s,
size_t pos=0,
bool to_lower=
false) {
121 void write_as_number(
const std::string& value) {
127 if (value[pos] ==
'+') {
129 }
else if (value[pos] ==
'-') {
134 while (value[pos] ==
'0' && std::isdigit(value[pos+1]))
137 size_t dotpos = value.find(
'.');
138 if (dotpos != std::string::npos && !std::isdigit(value[dotpos+1])) {
139 os_ << value.substr(pos, dotpos+1-pos) <<
'0';
142 if (value.back() !=
')')
143 os_ << value.c_str() + pos;
145 os_ << value.substr(pos, value.find(
'(', pos) - pos);
148 void write_value(
const std::string& value) {
151 else if (value ==
".")
155 (value[0] !=
'0' || value[1] ==
'.' || value[1] ==
'\0') &&
157 write_as_number(value);
162 void open_cat(
const std::string&
cat,
size_t* tag_pos) {
166 os_ <<
": {" << linesep_;
167 *tag_pos +=
cat.size() - 1;
171 void close_cat(std::string&
cat,
size_t* tag_pos) {
174 os_ << linesep_ <<
'}';
175 *tag_pos -=
cat.size() - 1;
180 void write_loop(
const Loop& loop) {
181 size_t ncol = loop.tags.size();
182 const auto& vals = loop.values;
183 std::string
cat = get_loop_category(loop);
185 open_cat(
cat, &tag_pos);
186 for (
size_t i = 0; i < ncol; i++) {
188 os_ <<
"," << linesep_;
191 for (
size_t j = i; j < vals.size(); j += ncol) {
194 write_value(vals[j]);
198 close_cat(
cat, &tag_pos);
203 void write_map(
const std::string& name,
const std::vector<Item>& items) {
208 bool has_frames =
false;
212 std::set<std::string> seen_cats;
213 for (
const Item& item : items) {
217 close_cat(
cat, &tag_pos);
218 os_ << first << linesep_;
220 cat = get_tag_category(item.pair[0]);
221 if (seen_cats.insert(
cat).second)
222 open_cat(
cat, &tag_pos);
228 write_value(item.pair[1]);
234 close_cat(
cat, &tag_pos);
235 os_ << first << linesep_;
236 write_loop(item.loop);
249 os_ << first << linesep_ <<
"\"Frames\": ";
252 for (
const Item& item : items)
254 os_ << first << linesep_;
255 write_map(item.frame.name, item.frame.items);
259 os_ << linesep_ <<
'}';
261 close_cat(
cat, &tag_pos);
263 os_ << linesep_ <<
'}';