mirror of
https://github.com/moparisthebest/mailiverse
synced 2024-11-12 04:05:10 -05:00
268 lines
11 KiB
C++
268 lines
11 KiB
C++
|
/******************************************************************************
|
||
|
|
||
|
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;
|
||
|
}
|