Click here to Skip to main content
6,291,124 members and growing! (15,870 online)
Email Password   helpLost your password?
General Programming » Algorithms & Recipes » Parsers     Intermediate License: The Code Project Open License (CPOL)

JSON Spirit: A C++ JSON Parser/Generator Implemented with Boost Spirit

By John W. Wilkinson

A C++ JSON parser/generator written using boost::spirit
C++, Windows, Visual Studio, Dev
Version:5 (See All)
Posted:15 Aug 2007
Updated:17 Jun 2009
Views:137,065
Bookmarked:71 times
Announcements
Loading...
 
Search    
Advanced Search
printPrint   Broken Article?Report       add Share
  Discuss Discuss   Recommend Article Email
39 votes for this article.
Popularity: 7.31 Rating: 4.60 out of 5
2 votes, 5.1%
1
1 vote, 2.6%
2
3 votes, 7.7%
3
2 votes, 5.1%
4
31 votes, 79.5%
5

Introduction

JSON is a text file format similar to XML, but less verbose. It has been called "XML lite". This article describes JSON Spirit, a C++ library that reads and writes JSON files or streams. It is written using the Boost Spirit parser generator. If you are already using Boost, you can use JSON Spirit without any additional dependencies.

The library supports Unicode. A new feature for version 4.0 is the option to use an std::map implementation for JSON Objects instead of the original std::vector implementation.

The JSON Spirit source code is available as a Microsoft Visual Studio 2005 C++ "solution". However, it should compile and work on any platform compatible with Boost. JSON Spirit has been built and tested with Visual C++ 2005, 2008, and G++ version 4.2.3 on Linux. It has been tested with Visual C++ using Boost versions 1.34.0, 1.37.0 and 1.39.0. It has also been tested with STLPort.

Platform independent CMake files are included, kindly supplied by Uwe Arzt.

The Visual C++ solution consists of four projects:

  • The JSON Spirit library
  • Two small programs demonstrating how to use JSON Spirit
  • An application running the library's unit tests

Using the Code

The Visual C++ solution builds an object library. You can link to this object library or, alternately, you can add the JSON Spirit source files directly to your project. All JSON Spirit declarations are in the namespace json_spirit.

The include files needed to read JSON text are json_spirit_reader.h and json_spirit_value.h. To generate JSON text, you need json_spirit_writer.h and json_spirit_value.h. Alternately, you can include json_spirit.h, which includes all three of the above.

Reading JSON

You can read JSON data from a stream or a string:

bool read( const std::string&  s, Value& value );
bool read( std::istream&  is,     Value& value );

For example:

ifstream is( "json.txt" );
Value value;
read( is, value );

You can also read JSON data by supplying a pair of string iterators.

bool read( std::string::const_iterator& begin,
           std::string::const_iterator  end, Value& value );

After a successful read, the iterator "begin" will point one past the last character of the text for the object just read. This allows the decoding of a string containing multiple top level objects. A subsequent call to read will read the next object in the string.

Similarly the stream reading functions now allow a sequence of top-level objects to be read one at a time. Previously a stream was converted to a string before being parsed. This was fine for files, but not if for example you want to read multiple JSON values from a socket.

JSON Spirit Value

A JSON value can hold either a JSON array, JSON object, string, integer, double, bool , or null. The interface of the JSON Spirit Value class is shown below. The Value class for Unicode is analogous; for details, see the section on Unicode support.

enum Value_type{ obj_type, array_type, str_type,
     bool_type, int_type, real_type, null_type };

class Value
{
    public:

        Value();  // creates null value

        Value( const char*          value );
        Value( const std::string&   value );
        Value( const Object&        value );
        Value( const Array&         value );
        Value( bool                 value );
        Value( int                  value );
        Value_impl( boost::int64_t  value );
        Value_impl( boost::uint64_t value );
        Value( double               value );

        bool operator==( const Value& lhs ) const;

        Value_type type() const;

        const std::string& get_str()   const;
        const Object&      get_obj()   const;
        const Array&       get_array() const;
        bool               get_bool()  const;
        int                get_int()   const;
        boost::int64_t     get_int64()  const;
        boost::uint64_t    get_uint64() const;
        double             get_real()  const;

        Object& get_obj();
        Array&  get_array();

        template< typename > T get_value() const;

        bool is_uint64() const;
        bool is_null() const;

        static const Value null;

    private:

        ...
};

You obtain the Value's type by calling Value::type(). You can then call the appropriate getter function. Generally, you will know a file's format, so you will know what type the JSON values should have.

The template getter function get_value() is an alternative to get_int(), get_real(), etc. Example usage would be:

int    i = value_1.get_value< int >();
double d = value_2.get_value< double >();

A top level Value read from a file or stream normally contains an Array or an Object. An Array is a std::vector of values. An Object is, by default, a std::vector of JSON pairs.

typedef std::vector< Pair > Object;
typedef std::vector< Value > Array;

A Pair is a structure that holds a std::string and a Value.

struct Pair
{
    Pair( const std::string& name, const Value& value );

    bool operator==( const Pair& lhs ) const;

    std::string name_;
    Value value_;
};

JSON Arrays and Objects can themselves contain other Arrays or Objects, forming a tree.

JSON Spirit provide an alternative std::map based Object, see the Map Implementation section below.

Writing JSON

To output JSON, you first create a Value object containing your data, then, write the created Value to a stream or string. There are two versions of each function: one outputs the JSON data without any white-space, the other formats the data by adding white-space and line breaks.

void        write          ( const Value& value, std::ostream& os );
void        write_formatted( const Value& value, std::ostream& os );
std::string write          ( const Value& value );
std::string write_formatted( const Value& value );

The following example shows how to create a small JSON file containing an object with three members:

Object addr_obj;

addr_obj.push_back( Pair( "house_number", 42 ) );
addr_obj.push_back( Pair( "road",         "East Street" ) );
addr_obj.push_back( Pair( "town",         "Newtown" ) );

ofstream os( "address.txt" );

write_formatted( addr_obj, os );

os.close();

The object addr_obj is automatically converted into a Value as it is passed to write_formatted. The file address.txt will contain:

{
    "house_number" : 42,
    "road" : "East Street",
    "town" : "Newtown"
}

Unicode Support

Unicode support is provided by std::wstring versions of the JSON Spirit Value, Array, Object, and Pair types. These are called wValue, wArray, wObject, and wPair. There are also std::wstring versions of each reader and writer function.

Note that there is no support for reading Unicode files and converting them to wstrings as this is not a task specific to JSON.

The Value and wValue classes are actually instantiations of the template class Value_impl.

Std::map Implementation

Before version 4.00 the JSON Spirit Object type was a std::vector of name/value Pairs. You now have the option of using mObject which is a name/value std::map. For the std::map version, use mValue instead of Value, mObject instead of Object and mArray instead of Array. For the Unicode map version, use wmValue, wmObject and wmArray.

The following table shows the times in seconds it takes on my PC to read a single object of varying sizes. The methods used are as per the demo programs. The vector version is faster until the number of object members reached around 10 but then gets exponentially slower.

size vector map
2 2.03253e-007 3.24672e-007
5 7.63788e-007 9.59516e-007
10 2.26948e-006 2.00929e-006
15 4.68965e-006 3.28509e-006
20 8.19667e-006 4.75871e-006
30 1.72695e-005 7.64189e-006
50 4.63171e-005 1.39316e-005
75 0.000102418 2.27205e-005
100 0.000179923 3.17732e-005
200 0.000709335 7.3061e-005
500 0.00440386 0.00022076
1000 0.0175385 0.000495554
10000 1.76533 0.00658268
100000 178.718 0.127325
1000000 17800.718 1.99467

Note with a vector object members will be written out in the same order they were read in. A map will sort members alphabetically. A vector object also allows members to have duplicate names. This might be useful in some circumstances but would be non-standard.

Error Detection

From version 3.00, JSON Spirit provides functions that report the position of format errors in text being parsed. These functions are identical to the normal read functions except that instead of returning false if an error is found, they throw a json_spirit::Error_position exception. Note, these functions run about three times slower than the normal read functions.

The Error_position structure holds the line and column number where the first error was found.

struct Error_position
{
    ...
    unsigned int line_;
    unsigned int column_;
    std::string reason_;
};

Using JSON Spirit with Multiple Threads

If you intend to use JSON Spirit in more than one thread, you will need to uncomment the following line near the top of json_spirit_reader.cpp.

//#define BOOST_SPIRIT_THREADSAFE

In this case, Boost Spirit will require you to link against Boost Threads.

History

  • 10th August 2007, Version 1.00.
  • 12th August 2007
    • Part of the Boost Spirit Applications Repository
  • 20th August 2007, Version 1.01.
    • Fixed bug outputting escape characters
  • 9th October 2007, Version 1.02.
    • Fixed bug inputting "\/"
    • No longer attaches a semantic action c_ecscape_ch_p, simplifying the code
    • Speed optimizations
  • 9th November 2007, Version 2.00.
    • Unicode support
    • Writes out "/" without a "\" escape character
  • 14th December 2007, Version 2.01.
    • Increased precision of floating point number output
  • 12th February 2008, Version 2.02, bug fixes.
    • Value construction from explicit const char*
    • Writes out extended ASCII
  • 1st March 2008, Version 2.03.
    • Added support for 64-bit integers
  • 22nd April 2008, Version 2.04.
    • Allows the reading of top level values other than Arrays and Objects, see Daniel Friederich's message thread below
    • Fixed bug where. e.g.. "[ 1 2 ]" is read as "[ 1, 2 ]"
    • Added template getter function get_value()
  • 2nd June 2008, Version 2.05.
    • Linux version added
    • Commented out #define BOOST_SPIRIT_THREADSAFE
  • 12th September 2008, Version 2.06.
    • Now works when MSVC 2005 is using STLPort
  • 31st January 2009, Version 3.00.
    • Multiple top-level objects can now be read from a string or stream
    • Addition of functions that take a pair of string iterators
    • Addition of functions that report the position of format errors
    • Speed optimisations
    • Added utility functions obj_to_map, map_to_obj, find_value
  • 11th May 2009, Version 3.01.
    • Added CMake files, many thanks to Uwe Arzt for supplying these
    • json_spirit_writer_test.cpp now includes limits.h, needed on some platforms
  • 9th June 2009, Version 4.00.
    • Added alternative std::map implementation for objects
    • Added support for unsigned 64 bit integers
    • Allow numbers such as 3 to be read as integers or floating point numbers

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

John W. Wilkinson


Member

Occupation: Software Developer (Senior)
Location: United Kingdom United Kingdom

Other popular Algorithms & Recipes articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 25 of 157 (Total in Forum: 157) (Refresh)FirstPrevNext
GeneralJSON Spirit ebuild for gentoo linux Pinmemberkukyakya3:38 3 Jul '09  
GeneralRe: JSON Spirit ebuild for gentoo linux PinmemberJohn W. Wilkinson23hrs 4mins ago 
GeneralRe: JSON Spirit ebuild for gentoo linux Pinmemberkukyakya7hrs 43mins ago 
Generalfix warning Pinmemberosy23:25 25 Jun '09  
GeneralRe: fix warning PinmemberJohn W. Wilkinson10:28 26 Jun '09  
General<< operator overloading Pinmemberjobarjo23:08 22 Jun '09  
GeneralRe: << operator overloading PinmemberJohn W. Wilkinson10:32 23 Jun '09  
Generaljson_demo project missing an include directory? Pinmemberdiewald11:50 22 Jun '09  
GeneralRe: json_demo project missing an include directory? PinmemberJohn W. Wilkinson10:28 23 Jun '09  
GeneralRe: json_demo project missing an include directory? Pinmemberdiewald11:37 23 Jun '09  
GeneralRe: json_demo project missing an include directory? PinmemberJohn W. Wilkinson10:08 24 Jun '09  
QuestionAssertion in get_real Pinmemberdiewald8:55 8 Jun '09  
AnswerRe: Assertion in get_real PinmemberJohn W. Wilkinson9:45 8 Jun '09  
GeneralRe: Assertion in get_real Pinmemberdiewald11:29 8 Jun '09  
Generalunresolved symbol error Pinmemberacoroian14:18 21 May '09  
GeneralRe: unresolved symbol error PinmemberJohn W. Wilkinson9:50 22 May '09  
GeneralRe: unresolved symbol error Pinmemberacoroian9:59 22 May '09  
QuestionHow to write and read size_t, INT_PTR and unsigned long int PinmemberDevil_Ashutosh20:53 20 May '09  
AnswerRe: How to write and read size_t, INT_PTR and unsigned long int PinmemberJohn W. Wilkinson10:16 21 May '09  
Generalbroken link to boost spirit PinmemberAndreone7:39 13 May '09  
GeneralRe: broken link to boost spirit PinmemberJohn W. Wilkinson10:02 13 May '09  
Generalcmake Files / Mac OS X build Pinmemberuwe.arzt9:53 25 Mar '09  
GeneralRe: cmake Files / Mac OS X build PinmemberJohn W. Wilkinson11:14 26 Mar '09  
AnswerFYI:Modifications for Linux PinmemberMark Gerrior11:41 20 Feb '09  
GeneralRe: FYI:Modifications for Linux PinmemberJohn W. Wilkinson9:42 22 Feb '09  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 17 Jun 2009
Editor: Deeksha Shenoy
Copyright 2007 by John W. Wilkinson
Everything else Copyright © CodeProject, 1999-2009
Web12 | Advertise on the Code Project