Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: CPL - Common Portability Library
4 : * Purpose: JSon streaming writer
5 : * Author: Even Rouault, even.rouault at spatialys.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
9 : *
10 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : /*! @cond Doxygen_Suppress */
30 :
31 : #include <vector>
32 : #include <string>
33 :
34 : #include "cpl_conv.h"
35 : #include "cpl_string.h"
36 : #include "cpl_json_streaming_writer.h"
37 :
38 50 : CPLJSonStreamingWriter::CPLJSonStreamingWriter(
39 50 : SerializationFuncType pfnSerializationFunc, void *pUserData)
40 50 : : m_pfnSerializationFunc(pfnSerializationFunc), m_pUserData(pUserData)
41 : {
42 50 : }
43 :
44 50 : CPLJSonStreamingWriter::~CPLJSonStreamingWriter()
45 : {
46 50 : CPLAssert(m_nLevel == 0);
47 50 : CPLAssert(m_states.empty());
48 50 : }
49 :
50 7969 : void CPLJSonStreamingWriter::Print(const std::string &text)
51 : {
52 7969 : if (m_pfnSerializationFunc)
53 : {
54 1411 : m_pfnSerializationFunc(text.c_str(), m_pUserData);
55 : }
56 : else
57 : {
58 6558 : m_osStr += text;
59 : }
60 7969 : }
61 :
62 0 : void CPLJSonStreamingWriter::SetIndentationSize(int nSpaces)
63 : {
64 0 : CPLAssert(m_nLevel == 0);
65 0 : m_osIndent.clear();
66 0 : m_osIndent.resize(nSpaces, ' ');
67 0 : }
68 :
69 531 : void CPLJSonStreamingWriter::IncIndent()
70 : {
71 531 : m_nLevel++;
72 531 : if (m_bPretty)
73 529 : m_osIndentAcc += m_osIndent;
74 531 : }
75 :
76 531 : void CPLJSonStreamingWriter::DecIndent()
77 : {
78 531 : CPLAssert(m_nLevel > 0);
79 531 : m_nLevel--;
80 531 : if (m_bPretty)
81 529 : m_osIndentAcc.resize(m_osIndentAcc.size() - m_osIndent.size());
82 531 : }
83 :
84 1408 : std::string CPLJSonStreamingWriter::FormatString(const std::string &str)
85 : {
86 1408 : std::string ret;
87 1408 : ret += '"';
88 18762 : for (char ch : str)
89 : {
90 17354 : switch (ch)
91 : {
92 608 : case '"':
93 608 : ret += "\\\"";
94 608 : break;
95 2 : case '\\':
96 2 : ret += "\\\\";
97 2 : break;
98 2 : case '\b':
99 2 : ret += "\\b";
100 2 : break;
101 2 : case '\f':
102 2 : ret += "\\f";
103 2 : break;
104 165 : case '\n':
105 165 : ret += "\\n";
106 165 : break;
107 2 : case '\r':
108 2 : ret += "\\r";
109 2 : break;
110 2 : case '\t':
111 2 : ret += "\\t";
112 2 : break;
113 16571 : default:
114 16571 : if (static_cast<unsigned char>(ch) < ' ')
115 2 : ret += CPLSPrintf("\\u%04X", ch);
116 : else
117 16569 : ret += ch;
118 16571 : break;
119 : }
120 : }
121 1408 : ret += '"';
122 1408 : return ret;
123 : }
124 :
125 2307 : void CPLJSonStreamingWriter::EmitCommaIfNeeded()
126 : {
127 2307 : if (m_bWaitForValue)
128 : {
129 940 : m_bWaitForValue = false;
130 : }
131 1367 : else if (!m_states.empty())
132 : {
133 1319 : if (!m_states.back().bFirstChild)
134 : {
135 797 : Print(",");
136 797 : if (m_bPretty && !m_bNewLineEnabled)
137 88 : Print(" ");
138 : }
139 1319 : if (m_bPretty && m_bNewLineEnabled)
140 : {
141 1181 : Print("\n");
142 1181 : Print(m_osIndentAcc);
143 : }
144 1319 : m_states.back().bFirstChild = false;
145 : }
146 2307 : }
147 :
148 307 : void CPLJSonStreamingWriter::StartObj()
149 : {
150 307 : EmitCommaIfNeeded();
151 307 : Print("{");
152 307 : IncIndent();
153 307 : m_states.emplace_back(State(true));
154 307 : }
155 :
156 307 : void CPLJSonStreamingWriter::EndObj()
157 : {
158 307 : CPLAssert(!m_bWaitForValue);
159 307 : CPLAssert(!m_states.empty());
160 307 : CPLAssert(m_states.back().bIsObj);
161 307 : DecIndent();
162 307 : if (!m_states.back().bFirstChild)
163 : {
164 301 : if (m_bPretty && m_bNewLineEnabled)
165 : {
166 286 : Print("\n");
167 286 : Print(m_osIndentAcc);
168 : }
169 : }
170 307 : m_states.pop_back();
171 307 : Print("}");
172 307 : }
173 :
174 224 : void CPLJSonStreamingWriter::StartArray()
175 : {
176 224 : EmitCommaIfNeeded();
177 224 : Print("[");
178 224 : IncIndent();
179 224 : m_states.emplace_back(State(false));
180 224 : }
181 :
182 224 : void CPLJSonStreamingWriter::EndArray()
183 : {
184 224 : CPLAssert(!m_states.empty());
185 224 : CPLAssert(!m_states.back().bIsObj);
186 224 : DecIndent();
187 224 : if (!m_states.back().bFirstChild)
188 : {
189 221 : if (m_bPretty && m_bNewLineEnabled)
190 : {
191 186 : Print("\n");
192 186 : Print(m_osIndentAcc);
193 : }
194 : }
195 224 : m_states.pop_back();
196 224 : Print("]");
197 224 : }
198 :
199 940 : void CPLJSonStreamingWriter::AddObjKey(const std::string &key)
200 : {
201 940 : CPLAssert(!m_states.empty());
202 940 : CPLAssert(m_states.back().bIsObj);
203 940 : CPLAssert(!m_bWaitForValue);
204 940 : EmitCommaIfNeeded();
205 940 : Print(FormatString(key));
206 940 : Print(m_bPretty ? ": " : ":");
207 940 : m_bWaitForValue = true;
208 940 : }
209 :
210 5 : void CPLJSonStreamingWriter::Add(bool bVal)
211 : {
212 5 : EmitCommaIfNeeded();
213 5 : Print(bVal ? "true" : "false");
214 5 : }
215 :
216 279 : void CPLJSonStreamingWriter::Add(const std::string &str)
217 : {
218 279 : EmitCommaIfNeeded();
219 279 : Print(FormatString(str));
220 279 : }
221 :
222 189 : void CPLJSonStreamingWriter::Add(const char *pszStr)
223 : {
224 189 : EmitCommaIfNeeded();
225 189 : Print(FormatString(pszStr));
226 189 : }
227 :
228 113 : void CPLJSonStreamingWriter::Add(std::int64_t nVal)
229 : {
230 113 : EmitCommaIfNeeded();
231 113 : Print(CPLSPrintf(CPL_FRMT_GIB, static_cast<GIntBig>(nVal)));
232 113 : }
233 :
234 160 : void CPLJSonStreamingWriter::Add(std::uint64_t nVal)
235 : {
236 160 : EmitCommaIfNeeded();
237 160 : Print(CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nVal)));
238 160 : }
239 :
240 18 : void CPLJSonStreamingWriter::Add(float fVal, int nPrecision)
241 : {
242 18 : EmitCommaIfNeeded();
243 18 : if (CPLIsNan(fVal))
244 : {
245 3 : Print("\"NaN\"");
246 : }
247 15 : else if (CPLIsInf(fVal))
248 : {
249 2 : Print(fVal > 0 ? "\"Infinity\"" : "\"-Infinity\"");
250 : }
251 : else
252 : {
253 : char szFormatting[10];
254 13 : snprintf(szFormatting, sizeof(szFormatting), "%%.%dg", nPrecision);
255 13 : Print(CPLSPrintf(szFormatting, fVal));
256 : }
257 18 : }
258 :
259 71 : void CPLJSonStreamingWriter::Add(double dfVal, int nPrecision)
260 : {
261 71 : EmitCommaIfNeeded();
262 71 : if (CPLIsNan(dfVal))
263 : {
264 35 : Print("\"NaN\"");
265 : }
266 36 : else if (CPLIsInf(dfVal))
267 : {
268 2 : Print(dfVal > 0 ? "\"Infinity\"" : "\"-Infinity\"");
269 : }
270 : else
271 : {
272 : char szFormatting[10];
273 34 : snprintf(szFormatting, sizeof(szFormatting), "%%.%dg", nPrecision);
274 34 : Print(CPLSPrintf(szFormatting, dfVal));
275 : }
276 71 : }
277 :
278 1 : void CPLJSonStreamingWriter::AddNull()
279 : {
280 1 : EmitCommaIfNeeded();
281 1 : Print("null");
282 1 : }
283 :
284 : /*! @endcond */
|