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