Home     Sign in    
A summer of accomplishments!
Spring Happenings
My Little Helpers
Archived News
(1) Where have all the good women gone?
(2) Declining divorce rates reveal the urgent need for legal reform
(3) The End of Car Ownership
(4) Oklahoma 911 caller: Three people broke into my house; I shot two
(5) If Hitler Had Won World War II Wed Have A Better, More Just World Today
Photo Archive
Browse by Folder
Browse by Calendar
JSON Parser
Neural Circuits
SIF Library

Simbey's C++ JSON Library

When I first encountered XML, I didn't find it that interesting.  Eventually I realized its value, but I was never overly excited about XML.  SOAP didn't even phase me.  It all seemed somehow mundane, boring, and limited.

Then I met JSON...  Even though both can accomplish the same tasks, JSON's structure and syntax were much more appealing to me.  I actually find JSON interesting, and I was excited to write a parser for it.  Yes, I did look at pre-existing parsers, but none worked quite the way I wanted.

My parser isn't going to work for everyone.  If you're looking for something that's cross-platform compatible using the STL, this isn't it.  Instead, I've built a Windows specific library that's COM-like: the library itself is a DLL, only interfaces and 'C' style APIs are exported, and all calls are cross-DLL safe.  Everything is reference counted, including the strings!
namespace JSON
	enum Type

interface IPreProcessorReportError
	virtual VOID ReportError (INT nLine, PCWSTR pcwzFile, PCWSTR pcwzError) = 0;

interface __declspec(uuid("C1CC5B0E-1210-481a-BC08-B56B77B5A421")) IJSONObject : IUnknown
	virtual sysint GetValueCount (VOID) = 0;
	virtual HRESULT GetValueName (sysint nValue, __out RSTRING* prstrName) = 0;
	virtual HRESULT GetValueByIndex (sysint nValue, __deref_out_opt IJSONValue** ppValue) = 0;
	virtual HRESULT SetValueByIndex (sysint nValue, IJSONValue* pValue) = 0;
	virtual HRESULT AddValue (RSTRING rstrName, __in_opt IJSONValue* pValue) = 0;
	virtual HRESULT AddValueW (PCWSTR pcwzName, __in_opt IJSONValue* pValue) = 0;
	virtual HRESULT RemoveValue (RSTRING rstrName, __deref_opt_out_opt IJSONValue** ppValue = NULL) = 0;
	virtual HRESULT RemoveValueW (PCWSTR pcwzName, __deref_opt_out_opt IJSONValue** ppValue) = NULL) = 0;
	virtual HRESULT FindValue (RSTRING rstrName, __deref_out_opt IJSONValue** ppValue) = 0;
	virtual HRESULT FindValueW (PCWSTR pcwzName, __deref_out_opt IJSONValue** ppValue) = 0;
	virtual HRESULT FindNonNullValue (RSTRING rstrName, __deref_out IJSONValue** ppValue) = 0;
	virtual HRESULT FindNonNullValueW (PCWSTR pcwzName, __deref_out IJSONValue** ppValue) = 0;
	virtual HRESULT Compact (VOID) = 0;

interface __declspec(uuid("817FAB75-C7A0-4894-B221-3132B34AC5AB")) IJSONValue : IUnknown
	virtual JSON::Type GetType (VOID) = 0;
	virtual HRESULT GetString (__out RSTRING* prstrValue) = 0;
	virtual HRESULT GetStringAsGuid (__out GUID& guid) = 0;
	virtual HRESULT GetObject (__deref_out IJSONObject** ppObject) = 0;
	virtual HRESULT GetArray (__deref_out IObjectCollectionEx** ppArray) = 0;
	virtual HRESULT GetArrayLength (__out sysint* pcArray) = 0;
	virtual HRESULT GetArrayItem (sysint nItem, __deref_out IJSONValue** ppValue) = 0;
	virtual HRESULT GetBoolean (__out bool* pfValue) = 0;
	virtual HRESULT GetInteger (__out int* pnValue) = 0;
	virtual HRESULT GetLongInteger (__out __int64* pnLongValue) = 0;
	virtual HRESULT GetDouble (__out double* pdblValue) = 0;
	virtual HRESULT GetFloat (__out float* pfltValue) = 0;
	virtual HRESULT GetDWord (__out DWORD* pdwValue) = 0;

// Core JSON parsing, serialization, and creation functions
HRESULT JSONParse (__in_opt IPreProcessorReportError* pReport, PCWSTR pcwzJSON, INT cchJSON,
	__deref_out IJSONValue** ppvJSON);
HRESULT JSONSerialize (__in_opt IJSONValue* pvJSON, __out ISequentialStream* pstmJSON);
INT JSONCompare (__in_opt IJSONValue* pvA, __in_opt IJSONValue* pvB);
HRESULT JSONClone (IJSONValue* pvJSON, __deref_out IJSONValue** ppvClone, BOOL fDeepClone);
HRESULT JSONWrapObject (IJSONObject* pObject, __deref_out IJSONValue** ppvJSON);
HRESULT JSONCreateObject (__deref_out IJSONObject** ppObject);

// Auxiliary path parsing JSON functions
HRESULT JSONGetObject (IJSONValue* pvRoot, PCWSTR pcwzPath, INT cchPath, BOOL fEnsureExists,
	__deref_out IJSONObject** ppObject, __in_opt IPreProcessorReportError* pReport = NULL);
HRESULT JSONGetValue (IJSONValue* pvRoot, PCWSTR pcwzPath, INT cchPath, __deref_out_opt IJSONValue** ppvJSON,
	__in_opt IPreProcessorReportError* pReport = NULL);
HRESULT JSONSetValue (IJSONValue* pvRoot, PCWSTR pcwzPath, INT cchPath, __in_opt IJSONValue* pvJSON,
	__in_opt IPreProcessorReportError* pReport = NULL);
HRESULT JSONRemoveValue (IJSONValue* pvRoot, PCWSTR pcwzPath, INT cchPath,
	__deref_opt_out_opt IJSONValue** ppvJSON, __in_opt IPreProcessorReportError* pReport = NULL);

You can download the 32-bit library here.  I haven't had the need yet to produce a 64-bit version, but I can produce one on demand if asked.

To use the library, just pass JSON text into JSONParse().  If successful, an object of type IJSONValue will be returned.  Most of the library should be fairly self explanatory.  JSONSerialize() formats JSON values back into text.  JSONCompare() compares two JSON values and returns -1, 0, or 1 depending on equivalence.  JSONClone() clones (makes a copy of) a value.  For non-deep clone requests, only the first level object or array is cloned.  For deep clones, all objects and arrays are cloned in the tree.  Values and strings are never copied, just reference counted.  JSONWrapObject() creates an IJSONValue wrapper for a provided object.  You'll need this when manually constructing objects.  JSONCreateObject() creates an empty object.

The JSONGetObject(), JSONGetValue(), JSONSetValue(), and JSONRemoveValue() APIs are convenience functions that use a path string to navigate to a location in the JSON tree.  The lexer used internally for these functions is the same lexer that's used for JSONParse().  Paths are names of fields separated by colons.  Each field name can be enclosed in quotes or not.  When a field name refers to an array, square brackets are used immediately after the field name to indicate which element of the array.  For example, this might be a valid path: food:fruit:apples[13].

Finally, RSTRING is probably new to you.  See RSTRING.h (in the download) for the full API listing.  You can almost think of RSTRING as a BSTR with reference counting.  Unlike a BSTR, which is always UTF-16 and can be referenced like PWSTR directly, RSTRING can be either UTF-8 or UTF-16 and cannot be referenced directly.  It can also wrap static strings without reference counting.  It does this using the first two bits of the pointer value itself.  Use the RStrIsStatic and RStrIsManaged macros to determine whether the memory is static or reference counted, and use the RStrIsAnsi and RStrIsWide macros to determine whether the memory is UTF-8 or UTF-16.  Perhaps RStrIsAnsi should have been named RStrIsUTF8 because it must be treated as UTF-8 text.  Finally, since RSTRING is typed as const BYTE*, you must treat RSTRING memory as immutable, and you cannot directly cast this memory to PSTR or PWSTR.  Instead, you should use the RStrToAnsi and RStrToWide macros to access the read-only memory.  The value NULL is a valid RSTRING value and indicates a static UTF-8 string that's NULL.  All non-NULL values used with RSTRING are zero terminated.  Like a BSTR, values can contain additional NIL characters, so you should always use RStrLen() to obtain the length of an RSTRING.  Finally, use the RStrAddRef() and RStrRelease() APIs on RSTRING values using COM rules.  When the reference falls to zero, the memory, which is owned by SimbeyCore.DLL (included in the download), is freed.

If you're wondering whether this library has ever been used professionally, I can proudly tell you it has!  I successfully used this JSON library in the Coca-Cola Freestyle's Consumer Engagement pilot program.

© 2001-2017 Simbey.com