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
CPLJSonStreamingWriter(SerializationFuncType pfnSerializationFunc,void * pUserData)38 CPLJSonStreamingWriter::CPLJSonStreamingWriter(
39 SerializationFuncType pfnSerializationFunc,
40 void* pUserData):
41 m_pfnSerializationFunc(pfnSerializationFunc),
42 m_pUserData(pUserData)
43 {}
44
~CPLJSonStreamingWriter()45 CPLJSonStreamingWriter::~CPLJSonStreamingWriter()
46 {
47 CPLAssert( m_nLevel == 0 );
48 CPLAssert( m_states.empty() );
49 }
50
Print(const std::string & text)51 void CPLJSonStreamingWriter::Print(const std::string& text)
52 {
53 if( m_pfnSerializationFunc )
54 {
55 m_pfnSerializationFunc(text.c_str(), m_pUserData);
56 }
57 else
58 {
59 m_osStr += text;
60 }
61 }
62
SetIndentationSize(int nSpaces)63 void CPLJSonStreamingWriter::SetIndentationSize(int nSpaces)
64 {
65 CPLAssert( m_nLevel == 0 );
66 m_osIndent.clear();
67 m_osIndent.resize(nSpaces, ' ');
68 }
69
IncIndent()70 void CPLJSonStreamingWriter::IncIndent()
71 {
72 m_nLevel ++;
73 if( m_bPretty )
74 m_osIndentAcc += m_osIndent;
75 }
76
DecIndent()77 void CPLJSonStreamingWriter::DecIndent()
78 {
79 CPLAssert(m_nLevel > 0);
80 m_nLevel --;
81 if( m_bPretty )
82 m_osIndentAcc.resize(m_osIndentAcc.size() - m_osIndent.size());
83 }
84
FormatString(const std::string & str)85 std::string CPLJSonStreamingWriter::FormatString(const std::string& str)
86 {
87 std::string ret;
88 ret += '"';
89 for( char ch: str )
90 {
91 switch(ch)
92 {
93 case '"' : ret += "\\\""; break;
94 case '\\': ret += "\\\\"; break;
95 case '\b': ret += "\\b"; break;
96 case '\f': ret += "\\f"; break;
97 case '\n': ret += "\\n"; break;
98 case '\r': ret += "\\r"; break;
99 case '\t': ret += "\\t"; break;
100 default:
101 if( static_cast<unsigned char>(ch) < ' ' )
102 ret += CPLSPrintf("\\u%04X", ch);
103 else
104 ret += ch;
105 break;
106 }
107 }
108 ret += '"';
109 return ret;
110 }
111
EmitCommaIfNeeded()112 void CPLJSonStreamingWriter::EmitCommaIfNeeded()
113 {
114 if( m_bWaitForValue )
115 {
116 m_bWaitForValue = false;
117 }
118 else if( !m_states.empty() )
119 {
120 if( !m_states.back().bFirstChild )
121 {
122 Print(",");
123 if( m_bPretty && !m_bNewLineEnabled )
124 Print(" ");
125 }
126 if( m_bPretty && m_bNewLineEnabled )
127 {
128 Print("\n");
129 Print(m_osIndentAcc);
130 }
131 m_states.back().bFirstChild = false;
132 }
133 }
134
StartObj()135 void CPLJSonStreamingWriter::StartObj()
136 {
137 EmitCommaIfNeeded();
138 Print("{");
139 IncIndent();
140 m_states.emplace_back(State(true));
141 }
142
EndObj()143 void CPLJSonStreamingWriter::EndObj()
144 {
145 CPLAssert(!m_bWaitForValue);
146 CPLAssert( !m_states.empty() );
147 CPLAssert( m_states.back().bIsObj );
148 DecIndent();
149 if( !m_states.back().bFirstChild )
150 {
151 if( m_bPretty && m_bNewLineEnabled )
152 {
153 Print("\n");
154 Print(m_osIndentAcc);
155 }
156 }
157 m_states.pop_back();
158 Print("}");
159 }
160
StartArray()161 void CPLJSonStreamingWriter::StartArray()
162 {
163 EmitCommaIfNeeded();
164 Print("[");
165 IncIndent();
166 m_states.emplace_back(State(false));
167 }
168
EndArray()169 void CPLJSonStreamingWriter::EndArray()
170 {
171 CPLAssert( !m_states.empty() );
172 CPLAssert( !m_states.back().bIsObj );
173 DecIndent();
174 if( !m_states.back().bFirstChild )
175 {
176 if( m_bPretty && m_bNewLineEnabled )
177 {
178 Print("\n");
179 Print(m_osIndentAcc);
180 }
181 }
182 m_states.pop_back();
183 Print("]");
184 }
185
AddObjKey(const std::string & key)186 void CPLJSonStreamingWriter::AddObjKey(const std::string& key)
187 {
188 CPLAssert( !m_states.empty() );
189 CPLAssert( m_states.back().bIsObj );
190 CPLAssert(!m_bWaitForValue);
191 EmitCommaIfNeeded();
192 Print(FormatString(key));
193 Print(m_bPretty ? ": " : ":");
194 m_bWaitForValue = true;
195 }
196
Add(bool bVal)197 void CPLJSonStreamingWriter::Add(bool bVal)
198 {
199 EmitCommaIfNeeded();
200 Print(bVal ? "true" : "false");
201 }
202
Add(const std::string & str)203 void CPLJSonStreamingWriter::Add(const std::string& str)
204 {
205 EmitCommaIfNeeded();
206 Print(FormatString(str));
207 }
208
Add(const char * pszStr)209 void CPLJSonStreamingWriter::Add(const char* pszStr)
210 {
211 EmitCommaIfNeeded();
212 Print(FormatString(pszStr));
213 }
214
Add(GIntBig nVal)215 void CPLJSonStreamingWriter::Add(GIntBig nVal)
216 {
217 EmitCommaIfNeeded();
218 Print(CPLSPrintf(CPL_FRMT_GIB, nVal));
219 }
220
Add(GUInt64 nVal)221 void CPLJSonStreamingWriter::Add(GUInt64 nVal)
222 {
223 EmitCommaIfNeeded();
224 Print(CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nVal)));
225 }
226
Add(float fVal,int nPrecision)227 void CPLJSonStreamingWriter::Add(float fVal, int nPrecision)
228 {
229 EmitCommaIfNeeded();
230 if( CPLIsNan(fVal) )
231 {
232 Print("\"NaN\"");
233 }
234 else if( CPLIsInf(fVal) )
235 {
236 Print( fVal > 0 ? "\"Infinity\"" : "\"-Infinity\"" );
237 }
238 else
239 {
240 char szFormatting[10];
241 snprintf(szFormatting, sizeof(szFormatting), "%%.%dg", nPrecision);
242 Print(CPLSPrintf(szFormatting, fVal));
243 }
244 }
245
Add(double dfVal,int nPrecision)246 void CPLJSonStreamingWriter::Add(double dfVal, int nPrecision)
247 {
248 EmitCommaIfNeeded();
249 if( CPLIsNan(dfVal) )
250 {
251 Print("\"NaN\"");
252 }
253 else if( CPLIsInf(dfVal) )
254 {
255 Print( dfVal > 0 ? "\"Infinity\"" : "\"-Infinity\"" );
256 }
257 else
258 {
259 char szFormatting[10];
260 snprintf(szFormatting, sizeof(szFormatting), "%%.%dg", nPrecision);
261 Print(CPLSPrintf(szFormatting, dfVal));
262 }
263 }
264
AddNull()265 void CPLJSonStreamingWriter::AddNull()
266 {
267 EmitCommaIfNeeded();
268 Print("null");
269 }
270
271 /*! @endcond */
272