diff options
Diffstat (limited to 'sca-cpp/trunk/modules/json/json.hpp')
-rw-r--r-- | sca-cpp/trunk/modules/json/json.hpp | 347 |
1 files changed, 277 insertions, 70 deletions
diff --git a/sca-cpp/trunk/modules/json/json.hpp b/sca-cpp/trunk/modules/json/json.hpp index 85579c1b28..b3545476a1 100644 --- a/sca-cpp/trunk/modules/json/json.hpp +++ b/sca-cpp/trunk/modules/json/json.hpp @@ -31,50 +31,243 @@ #include "value.hpp" #include "element.hpp" #include "monad.hpp" -#include "../js/eval.hpp" +#include "jansson.h" namespace tuscany { namespace json { /** - * Return true if a list of strings contains a JSON document. + * Customize JSON memory allocation functions. */ -const bool isJSON(const list<string>& ls) { - if (isNil(ls)) - return false; - const string s = substr(car(ls), 0, 1); - return s == "[" || s == "{"; +inline void* jsonAlloc(size_t size) { + return gc_pool_malloc(size); } +inline void jsonFree(void *ptr) { + return gc_pool_free(ptr); +} + +class JSONAllocFuncs { +public: + inline JSONAllocFuncs() { + json_set_alloc_funcs(jsonAlloc, jsonFree); + } +} jsonAllocFuncs; + /** - * Consumes JSON strings and populates a JS object. + * Returns true if a list represents a JS array. */ -failable<bool> consume(JSONParser* parser, const list<string>& ilist, const js::JSContext& cx) { - if (isNil(ilist)) +inline const bool isJSArray(const list<value>& l) { + if(isNil(l)) return true; - JSString* jstr = JS_NewStringCopyZ(cx, c_str(car(ilist))); - if(!JS_ConsumeJSONText(cx, parser, JS_GetStringCharsZ(cx, jstr), (uint32)JS_GetStringLength(jstr))) - return mkfailure<bool>("JS_ConsumeJSONText failed"); - return consume(parser, cdr(ilist), cx); + const value v = car(l); + if (isSymbol(v)) + return false; + if(isList(v)) { + if(!isNil((list<value>)v) && isSymbol(car<value>(v))) + return false; + } + return true; } /** - * Convert a list of strings representing a JSON document to a list of values. + * Converts a JS array to values. */ -const failable<list<value> > readJSON(const list<string>& ilist, const js::JSContext& cx) { - jsval val; - JSONParser* parser = JS_BeginJSONParse(cx, &val); - if(parser == NULL) - return mkfailure<list<value> >("JS_BeginJSONParse failed"); +inline const list<value> jsArrayToValues(const list<value>& listSoFar, json_t* const a, const int i) { + const value jsValToValue(json_t* const jsv); + + if (i < 0) + return listSoFar; + json_t* const jsv = json_array_get(a, i); + const value val = jsValToValue(jsv); + return jsArrayToValues(cons(val, listSoFar), a, i - 1); +} - const failable<bool> consumed = consume(parser, ilist, cx); +/** + * Converts JS properties to values. + */ +inline const list<value> jsPropertiesToValues(const list<value>& propertiesSoFar, json_t* const o, void* i) { + const value jsValToValue(json_t* const jsv); - if(!JS_FinishJSONParse(cx, parser, JSVAL_NULL)) - return mkfailure<list<value> >("JS_FinishJSONParse failed"); - if(!hasContent(consumed)) - return mkfailure<list<value> >(consumed); + if (i == NULL) + return reverse(propertiesSoFar); + const char* const name = json_object_iter_key(i); + json_t* const jsv = json_object_iter_value(i); + const value val = jsValToValue(jsv); + if (isNil(val) && !isList(val)) + return jsPropertiesToValues(cons<value> (mklist<value> (element, c_str(name), val), propertiesSoFar), o, json_object_iter_next(o, i)); + if (substr(name, 0, 1) == atsign) + return jsPropertiesToValues(cons<value>(mklist<value>(attribute, c_str(substr(name, 1)), val), propertiesSoFar), o, json_object_iter_next(o, i)); + if (isList(val) && !isJSArray(val)) + return jsPropertiesToValues(cons<value>(cons<value>(element, cons<value>(c_str(name), list<value>(val))), propertiesSoFar), o, json_object_iter_next(o, i)); + return jsPropertiesToValues(cons<value> (mklist<value> (element, c_str(name), val), propertiesSoFar), o, json_object_iter_next(o, i)); +} + +/** + * Converts a JS val to a value. + */ +const value jsValToValue(json_t* const jsv) { + switch(json_typeof(jsv)) { + case json_type::JSON_STRING: { + const string svalue = string(json_string_value(jsv)); + return value(svalue); + } + case json_type::JSON_TRUE: { + return value((bool)true); + } + case json_type::JSON_FALSE: { + return value((bool)false); + } + case json_type::JSON_REAL: { + return value((double)json_real_value(jsv)); + } + case json_type::JSON_INTEGER: { + return value((double)json_integer_value(jsv)); + } + case json_type::JSON_NULL: { + return nilValue; + } + case json_type::JSON_ARRAY: { + const int i = (int)json_array_size(jsv) - 1; + const value v = jsArrayToValues(list<value> (), jsv, i); + return v; + } + case json_type::JSON_OBJECT: { + void* const i = json_object_iter(jsv); + const value v = jsPropertiesToValues(list<value> (), jsv, i); + return v; + } + default: { + return nilValue; + } + } +} - return list<value>(js::jsValToValue(val, cx)); +/** + * Converts a list of values to JS array elements. + */ +inline json_t* const valuesToJSElements(json_t* const a, const list<value>& l) { + json_t* const valueToJSVal(const value& val); + if (isNil(l)) + return a; + json_t* const v = valueToJSVal(car(l)); + json_array_append(a, v); + return valuesToJSElements(a, cdr(l)); +} + +/** + * Converts a value to a JS val. + */ +inline json_t* const valueToJSVal(const value& val) { + json_t* const valuesToJSProperties(json_t* const o, const list<value>& l); + + switch(type(val)) { + case value::String: { + return json_string(c_str((string)val)); + } + case value::Symbol: { + return json_string(c_str((string)val)); + } + case value::Bool: { + return json_boolean((bool)val); + } + case value::Number: { + const double d = (double)val; + if (((double)((json_int_t)d)) == d) + return json_integer((json_int_t)d); + else + return json_real(d); + } + case value::List: { + if (isJSArray(val)) { + json_t* const a = json_array(); + return valuesToJSElements(a, val); + } + json_t* const c = json_object(); + return valuesToJSProperties(c, val); + } + case value::Nil: { + return json_null(); + } + default: { + return json_null(); + } + } +} + +/** + * Converts a list of values to JS properties. + */ +inline json_t* const valuesToJSProperties(json_t* const o, const list<value>& l) { + if (isNil(l)) + return o; + + // Write an attribute + const value token(car(l)); + + if (isTaggedList(token, attribute)) { + json_t* const v = valueToJSVal(attributeValue(token)); + json_object_set(o, c_str(atsign + string(attributeName(token))), v); + + } else if (isTaggedList(token, element)) { + + // Write the value of an element + if (elementHasValue(token)) { + json_t* const v = valueToJSVal(elementValue(token)); + json_object_set(o, c_str(string(elementName(token))), v); + + } else { + + // Write a parent element + json_t* const c = json_object(); + json_object_set(o, c_str(string(elementName(token))), c); + + // Write its children + valuesToJSProperties(c, elementChildren(token)); + } + } + + // Go on + return valuesToJSProperties(o, cdr(l)); +} + +/** + * Return true if a list of strings contains a JSON document. + */ +inline const bool isJSON(const list<string>& ls) { + if (isNil(ls)) + return false; + const string s = substr(car(ls), 0, 1); + return s == "[" || s == "{"; +} + + /** + * Convert a list of strings representing a JSON document to a list of elements. + */ +inline const failable<list<value> > readElements(const list<string>& ilist) { + ostringstream os; + write(ilist, os); + json_error_t e; + json_t* const val = json_loads(c_str(str(os)), 0, &e); + if(val == NULL) + return mkfailure<list<value> >(string("json_loads failed: ") + string(e.text)); + return list<value>(jsValToValue(val)); +} + +/** + * Convert a list of strings representing a JSON document to a value + */ +inline const failable<value> readValue(const list<string>& ilist) { + ostringstream os; + write(ilist, os); + json_error_t e; + json_t* const jv = json_loads(c_str(str(os)), JSON_DECODE_ANY, &e); + if(jv == NULL) + return mkfailure<value>(string("json_loads failed: ") + string(e.text)); + const value val = jsValToValue(jv); + if (!isList(val)) + return val; + return (value)elementsToValues(val); } /** @@ -82,47 +275,68 @@ const failable<list<value> > readJSON(const list<string>& ilist, const js::JSCon */ template<typename R> class WriteContext { public: - WriteContext(const lambda<R(const string&, const R)>& reduce, const R& accum, const js::JSContext& cx) : cx(cx), reduce(reduce), accum(accum) { + inline WriteContext(const lambda<const R(const string&, const R)>& reduce, const R& accum) : reduce(reduce), accum(accum) { } - const js::JSContext& cx; - const lambda<R(const string&, const R)> reduce; - R accum; + + const lambda<const R(const string&, const R)> reduce; + gc_mutable_ref<R> accum; }; /** - * Called by JS_Stringify to write JSON out. + * Called by dump_callback to write JSON out. */ -template<typename R> JSBool writeCallback(const jschar *buf, uint32 len, void *data) { - WriteContext<R>& wcx = *(static_cast<WriteContext<R>*> (data)); - JSString* jstr = JS_NewUCStringCopyN(wcx.cx, buf, len); - char* cstr = JS_EncodeString(wcx.cx, jstr); - const string str(cstr, JS_GetStringLength(jstr)); - JS_free(wcx.cx, cstr); +template<typename R> inline int writeCallback(const char *buf, size_t len, void *data) { + WriteContext<R>& wcx = *(WriteContext<R>*)data; + const string str(buf, len); wcx.accum = wcx.reduce(str, wcx.accum); - return JS_TRUE; + return 0; } /** - * Convert a list of values to a JSON document. + * Convert a value to a JSON document. */ -template<typename R> const failable<R> writeJSON(const lambda<R(const string&, const R)>& reduce, const R& initial, const list<value>& l, const js::JSContext& cx) { - jsval val; - if (js::isJSArray(l)) - val = OBJECT_TO_JSVAL(valuesToJSElements(JS_NewArrayObject(cx, 0, NULL), l, 0, cx)); - else - val = OBJECT_TO_JSVAL(valuesToJSProperties(JS_NewObject(cx, NULL, NULL, NULL), l, cx)); +template<typename R> inline const failable<R> writeValue(const lambda<const R(const string&, const R)>& reduce, const R& initial, const list<value>& l) { + json_t* const val = valueToJSVal(l); + WriteContext<R> wcx(reduce, initial); + if (json_dump_callback(val, writeCallback<R>, &wcx, JSON_COMPACT | JSON_ENSURE_ASCII | JSON_PRESERVE_ORDER | JSON_ENCODE_ANY) == -1) + return mkfailure<R>("json_dump_callback failed"); + return (R)wcx.accum; +} - WriteContext<R> wcx(reduce, initial, cx); - if (!JS_Stringify(cx, &val, NULL, INT_TO_JSVAL(1), writeCallback<R>, &wcx)) - return mkfailure<R>("JS_Stringify failed"); - return wcx.accum; +/** + * Convert a list of elements to a JSON document. + */ +template<typename R> inline const failable<R> writeElements(const lambda<const R(const string&, const R)>& reduce, const R& initial, const list<value>& l) { + json_t* const val = isJSArray(l)? valuesToJSElements(::json_array(), l) : valuesToJSProperties(::json_object(), l); + + WriteContext<R> wcx(reduce, initial); + if (json_dump_callback(val, writeCallback<R>, &wcx, JSON_COMPACT | JSON_ENSURE_ASCII | JSON_PRESERVE_ORDER | JSON_ENCODE_ANY) == -1) + return mkfailure<R>("json_dump_callback failed"); + return (R)wcx.accum; } /** - * Convert a list of values to a list of strings representing a JSON document. + * Convert a list of elements to a list of strings representing a JSON document. */ -const failable<list<string> > writeJSON(const list<value>& l, const js::JSContext& cx) { - const failable<list<string> > ls = writeJSON<list<string>>(rcons<string>, list<string>(), valuesToElements(elementsToValues(l)), cx); +inline const failable<list<string> > writeElements(const list<value>& l) { + const failable<list<string> > ls = writeElements<list<string>>(rcons<string>, list<string>(), valuesToElements(elementsToValues(l))); + if (!hasContent(ls)) + return ls; + return reverse(list<string>(content(ls))); +} + +/** + * Convert a value to a list of strings representing a JSON document. + */ +inline const failable<list<string> > writeValue(const value& v) { + if (!isList(v)) { + const failable<list<string> > ls = writeValue<list<string>>(rcons<string>, list<string>(), v); + if (!hasContent(ls)) + return ls; + return reverse(list<string>(content(ls))); + } + + const failable<list<string> > ls = writeElements<list<string>>(rcons<string>, list<string>(), valuesToElements(elementsToValues(valuesToElements(v)))); if (!hasContent(ls)) return ls; return reverse(list<string>(content(ls))); @@ -131,44 +345,37 @@ const failable<list<string> > writeJSON(const list<value>& l, const js::JSContex /** * Convert a list of function + params to a JSON-RPC request. */ -const failable<list<string> > jsonRequest(const value& id, const value& func, const value& params, js::JSContext& cx) { +inline const failable<list<string> > jsonRequest(const value& id, const value& func, const value& params) { const list<value> r = mklist<value>(mklist<value>("id", id), mklist<value>("method", string(func)), mklist<value>("params", params)); - return writeJSON(valuesToElements(r), cx); + return writeElements(valuesToElements(r)); } /** * Convert a value to a JSON-RPC result. */ -const failable<list<string> > jsonResult(const value& id, const value& val, js::JSContext& cx) { - return writeJSON(valuesToElements(mklist<value>(mklist<value>("id", id), mklist<value>("result", val))), cx); +inline const failable<list<string> > jsonResult(const value& id, const value& val) { + return writeElements(valuesToElements(mklist<value>(mklist<value>("id", id), mklist<value>("result", val)))); } /** * Convert a JSON-RPC result to a value. */ -const failable<value> jsonResultValue(const list<string>& s, js::JSContext& cx) { - const failable<list<value> > jsres = json::readJSON(s, cx); +inline const failable<value> jsonResultValue(const list<string>& s) { + const failable<value> jsres = json::readValue(s); if (!hasContent(jsres)) return mkfailure<value>(jsres); - const list<value> rval(cadr<value>(elementsToValues(content(jsres)))); + const list<value> rval(cadr<value>(content(jsres))); const value val = cadr(rval); - if (isList(val) && !js::isJSArray(val)) + if (isList(val) && !isJSArray(val)) return value(mklist<value>(val)); return val; } /** - * Convert a JSON payload to a list of values. - */ -const list<value> jsonValues(const list<value>& e) { - return elementsToValues(e); -} - -/** * Return a portable function name from a JSON-RPC function name. * Strip the ".", "system." and "Service." prefixes added by some JSON-RPC clients. */ -const string funcName(const string& f) { +inline const string funcName(const string& f) { if (length(f) > 1 && find(f, ".", 0) == 0) return c_str(f) + 1; if (length(f) > 7 && find(f, "system.", 0) == 0) @@ -182,9 +389,9 @@ const string funcName(const string& f) { * Returns a list of param values other than the id and method args from a list * of key value pairs. */ -const list<value> queryParams(const list<list<value> >& a) { +inline const list<value> queryParams(const list<list<value> >& a) { if (isNil(a)) - return list<value>(); + return nilListValue; const list<value> p = car(a); if (car(p) == value("id") || car(p) == value("method")) return queryParams(cdr(a)); |