File: test.cpp

package info (click to toggle)
cajun 2.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, buster, jessie, jessie-kfreebsd, sid, stretch
  • size: 180 kB
  • sloc: cpp: 433; makefile: 11
file content (267 lines) | stat: -rw-r--r-- 10,870 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/******************************************************************************

Copyright (c) 2009-2010, Terry Caton
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright 
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the projecct nor the names of its contributors 
      may be used to endorse or promote products derived from this software 
      without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

******************************************************************************/

#include "json/reader.h"
#include "json/writer.h"
#include "json/elements.h"

#include <sstream>


int main()
{
   using namespace json;

   /* we'll generate: 
      {
         "Delicious Beers" : [
            {
               "Name" : "Schlafly American Pale Ale",
               "Origin" : "St. Louis, MO, USA",
               "ABV" : 5.9,
               "BottleConditioned" : true
            },
            {
               "Name" : "John Smith's Extra Smooth",
               "Origin" : "Tadcaster, Yorkshire, UK",
               "ABV" : 3.8,
               "Bottle Conditioned" : false
            }
         ]
      }
   */

   ////////////////////////////////////////////////////////////////////
   // construction

   // we can build a document piece by piece...
   Object objAPA;
   objAPA["Name"] = String("Schlafly American Pale Ale");
   objAPA["Origin"] = String("St. Louis, MO, USA");
   objAPA["ABV"] = Number(3.8);
   objAPA["BottleConditioned"] = Boolean(true);

   Array arrayBeer;
   arrayBeer.Insert(objAPA);

   Object objDocument;
   objDocument["Delicious Beers"] = arrayBeer;

   Number numDeleteThis = objDocument["AnotherMember"];

   // ...or, we can use UnknownElement's chaining child element access to quickly
   //  construct the remainder

   objDocument["Delicious Beers"][1]["Name"] = String("John Smith's Extra Smooth");
   objDocument["Delicious Beers"][1]["Origin"] = String("Tadcaster, Yorkshire, UK");
   objDocument["Delicious Beers"][1]["ABV"] = Number(3.8);
   objDocument["Delicious Beers"][1]["BottleConditioned"] = Boolean(false);

   
   ////////////////////////////////////////////////////////////////////
   // interpretation
      
   // perform all read operations on a const ref, otherwise we may end up
   //  manipulating the document instead of catching errors
   const Object& objRoot = objDocument;

   // the return type of Object::operator[string] & Array::operator[size_t] is UnknownElement, which 
   //  provides implicit casting to any of the other element types...
   const Array& arrayBeers = objRoot["Delicious Beers"];
   const Object& objBeer0 = arrayBeers[0];
   const String& strName0 = objBeer0["Name"];

   // ...it also supports operator[string] & operator[size_t] itself, which takes the implicit casting
   //  one step further. operator[string] implicitly casts to Object, and operator[size_t] to Array.
   //  the return value is another UnknownElement, so these operations can be strung together
   const Number numAbv1 = objRoot["Delicious Beers"][1]["ABV"];

   std::cout << "First beer name: " << strName0.Value() << std::endl;
   std::cout << "First beer ABV: " << numAbv1.Value() << std::endl;

   // we can also iterate through the child elements of an array or object, which is necessary
   //  when we don't know the structure of the document
   Array::const_iterator itBeers(arrayBeers.Begin()),
                         itBeersEnd(arrayBeers.End());
   for (; itBeers != itBeersEnd; ++itBeers)
   {
      // remember, *itArray is an UnknownElement, which can be implicitly cast to another element type
      const Object& objBeer = *itBeers;
      Object::const_iterator itBeerFacts(objBeer.Begin()),
                             itBeerFactsEnd(objBeer.End());
      for (; itBeerFacts != itBeerFactsEnd; ++itBeerFacts)
      {
         const Object::Member& member = *itBeerFacts;
         const std::string& name = member.name;
         const UnknownElement& element = member.element;

         // if we didn't know the structure of the itBeerFacts subtree, we could visit it
         // element.Accept(nonExistantVisitor);
      }
   }

   // everything's cool until we try to access a non-existent array element
   try
   {
      std::cout << "Expecting exception: Array out of bounds" << std::endl;
      const String& strName2 = arrayBeers[2];
   }
   catch (const Exception& e)
   {
      std::cout << "Caught json::Exception: " << e.what() << std::endl << std::endl;
   }

   // an exception will be thrown when expected data not found, since "Rice" is never a member of good beer
   try 
   {
      std::cout << "Expecting exception: Object member not found" << std::endl;
      const Boolean& boolRice = objRoot["Delicious Beers"][1]["Rice"];
   }
   catch (const Exception& e)
   {
      std::cout << "Caught json::Exception: " << e.what() << std::endl << std::endl;
   }

   // we'll also get an error if the document structure isn't quite what we expect
   try 
   {
      // objRoot["Delicious Beers"] is an Array, not another Object, so the second chained operator[string] will fail
      std::cout << "Expecting exception: Bad cast" << std::endl;
      const UnknownElement& elem = objRoot["Delicious Beers"]["Some Object Member"];
   }
   catch (json::Exception& e)
   {
      std::cout << "Caught json::Exception: " << e.what() << std::endl << std::endl;
   }


   ////////////////////////////////////////////////////////////////////
   // document deep copying
    
   // we can make an exact duplicate too
   Object objRoot2 = objRoot; 

   // the two documents should start out equal
   bool bEqualInitially = (objRoot == objRoot2);
   std::cout << "Document copies should start out equivalent. operator == returned: "
             << (bEqualInitially ? "true" : "false") << std::endl;

   // prove objRoot2 is a deep copy of objRoot:
   //  remove Beers[1]
   Array& array = objRoot2["Delicious Beers"];
   array.Erase(array.Begin()); // trim it down to one. this leaves elemRoot the same

   // the two documents should start out equal
   bool bEqualNow = (objRoot == objRoot2);
   std::cout << "Document copies should now be different. operator == returned: "
             << (bEqualNow ? "true" : "false") << std::endl << std::endl;


   ////////////////////////////////////////////////////////////////////
   // read/write sanity check

   // write it out to a string stream (file stream would work the same)....
   std::cout << "Writing file out...";

   std::stringstream stream;
   Writer::Write(objRoot, stream);

   // ...then read it back in. we know it's an Object
   std::cout << "then reading it back in." << std::endl;
   Object elemRootFile;
   Reader::Read(elemRootFile, stream);

   // still look right?
   bool bEquals = (objRoot == elemRootFile);
   std::cout << "Original document and streamed document should be equivalent. operator == returned: "
      << (bEquals ? "true" : "false") << std::endl << std::endl;


   ////////////////////////////////////////////////////////////////////
   // document read error handling

   // mis-predicting type type will fail with a parse error. we'll try reading an array into an object
   try
   {
      std::istringstream sBadDocument("[1, 2]"); // missing comma!
      std::cout << "Reading Object-based document into an Array; expecting Parse exception" << std::endl;
      Object objDocument;
      Reader::Read(objDocument, sBadDocument);
   }
   catch (Reader::ParseException& e)
   {
      // lines/offsets are zero-indexed, so bump them up by one for human presentation
      std::cout << "Caught json::ParseException: " << e.what() << ", Line/offset: " << e.m_locTokenBegin.m_nLine + 1
                << '/' << e.m_locTokenBegin.m_nLineOffset + 1 << std::endl << std::endl;
   }

   // reading in a slightly malformed document may result in a parse error
   try
   {
      std::istringstream sBadDocument("[1, 2 3]"); // missing comma!
      std::cout << "Reading malformed document; expecting Parse exception" << std::endl;
      Array arrayDocument;
      Reader::Read(arrayDocument, sBadDocument);
   }
   catch (Reader::ParseException& e)
   {
      std::cout << "Caught json::ParseException: " << e.what() << ", Line/offset: " << e.m_locTokenBegin.m_nLine + 1
                << '/' << e.m_locTokenBegin.m_nLineOffset + 1 << std::endl << std::endl;
   }

   // reading in gibberish will generate a scan error
   try
   {
      std::istringstream sBadDocument("[true, false, true, #.&@k*k4L!`1");
      std::cout << "Reading complete garbage; expecting Scan exception" << std::endl;
      Array arrayDocument;
      Reader::Read(arrayDocument, sBadDocument);
   }
   catch (Reader::ScanException& e)
   {
      std::cout << "Caught json::ScanException: " << e.what() << ", Line/offset: " << e.m_locError.m_nLine + 1
                << '/' << e.m_locError.m_nLineOffset + 1 << std::endl << std::endl;
   }

   try {
      std::istringstream sIncompleteDocument("[ true, ");
      std::cout << "Reading incomplete document; expecting Parse exception" << std::endl;

      json::Array arrayDocument;
      json::Reader::Read(arrayDocument, sIncompleteDocument);
   }
   catch (Reader::ParseException& e)
   {
      std::cout << "Caught json::ParseException: " << e.what() << ", Line/offset: " << e.m_locTokenBegin.m_nLine + 1
                << '/' << e.m_locTokenBegin.m_nLineOffset + 1 << std::endl << std::endl;
   }


   return 0;
}