SpiderMonkeyでJSON読み込み

あまり使われていないのか情報は少なかったが
SpiderMonkeyを使用してJSON形式を読み込む方法の調べがついた。
文字列、数字、配列、オブジェクト、NULL, BOOLEAN と普通に使う種類のはOK。
オブジェクトと配列のネストも問題なし。

標準入力からJSONを読み込むサンプル

#include <iostream>
#include <string>
#include <sstream>

#include <js/jsapi.h>

JSRuntime *pRuntime;
JSContext *pContext;
JSObject *pGlobal;

void js_print(jsval jsVal, int indentLevel)
{
  // indent
  std::cout << std::string(indentLevel*2, ' ');

  if(JSVAL_IS_NULL(jsVal)){
    std::cout << "NULL" << std::endl;
  }
  else if(JSVAL_IS_OBJECT(jsVal)){
    if(JS_IsArrayObject(pContext, JSVAL_TO_OBJECT(jsVal))){
      // Array
      std::cout << "ARRAY:[" << std::endl;
      JSObject *array=JSVAL_TO_OBJECT(jsVal);
      jsuint length;
      if(!JS_GetArrayLength(pContext, array, &length))
        return;
      for(int i=0; i<length; i++){
        jsval vp;
        JS_GetElement(pContext, array, i, &vp);
        js_print(vp, indentLevel+1);
      }
      std::cout << std::string(indentLevel*2, ' ') << "]" << std::endl;
    }
    else{
      // Object
      std::cout << "OBJECT:{" << std::endl;
      JSObject *it=JS_NewPropertyIterator(pContext, JSVAL_TO_OBJECT(jsVal));
      jsval keyId;
      jsval keyVal;
      jsval propVal;
      JSString *keyStr;
      while(JS_NextProperty(pContext, it, &keyId)){
        if(keyId==JSVAL_VOID)
          break;
        JS_IdToValue(pContext, keyId, &keyVal);
        keyStr=JS_ValueToString(pContext, keyVal);
        std::cout << std::string((indentLevel+1)*2, ' ') << JS_GetStringBytes(keyStr) << "=>" << std::endl; 
        JS_GetProperty(pContext, JSVAL_TO_OBJECT(jsVal), JS_GetStringBytes(keyStr), &propVal);
        js_print(propVal, indentLevel+2);
      }
      std::cout << std::string(indentLevel*2, ' ') << "}" << std::endl;
    }
  }
  else if(JSVAL_IS_PRIMITIVE(jsVal)){
    std::cout << "PRIMITIVE:"; 
    if(JSVAL_IS_NUMBER(jsVal)){
      std::cout << "NUMBER:"; 
      if(JSVAL_IS_INT(jsVal)){
        std::cout << "INT:" << JSVAL_TO_INT(jsVal);
      }
      if(JSVAL_IS_DOUBLE(jsVal)){
        std::cout << "DOUBLE:" << *JSVAL_TO_DOUBLE(jsVal); 
      }
    }
    if(JSVAL_IS_STRING(jsVal)){
      JSString *str=JS_ValueToString(pContext, jsVal);
      std::cout << "STRING:\"" << JS_GetStringBytes(str) << "\""; 
    }
    if(JSVAL_IS_BOOLEAN(jsVal)){
      std::cout << "BOOLEAN:" << JSVAL_TO_BOOLEAN(jsVal); 
    }
    std::cout << std::endl;
  }
  else{
    std::cout << "else:" << std::endl;
  }
}

int main(int argc, char** argv)
{
  // SpiderMokeyの初期化
  pRuntime = JS_NewRuntime(8L * 1024L * 1024L);
  if(!pRuntime)
    return 1;

  // contextの作成
  pContext = JS_NewContext(pRuntime, 8192);
  if(!pContext)
    return 2;

  // global objectの初期化
  pGlobal = JS_NewObject(pContext, NULL, 0, 0);
  if(!pGlobal)
    return 3;

  // builtin classの初期化
  if(!JS_InitStandardClasses(pContext, pGlobal))
    return 4;

  // 標準入力からJSONデータを読み込む
  std::string line;
  std::stringstream buf;
  buf << "var json=";
  while(std::getline(std::cin, line)){
    //std::cout << line << std::endl;
    buf << line << std::endl;
  }
  buf << ";";

  // JSONを評価する
  jsval jsVal;
  if(!JS_EvaluateScript(pContext, pGlobal, buf.str().c_str(), buf.str().size(), NULL, 0, &jsVal))
    return 5; 
  
  // 評価した変数をゲットする
  if (!JS_GetProperty(pContext, pGlobal, "json", &jsVal))
    return 6;

  // 読み込んだ変数を表示する
  js_print(jsVal, 0);
  
  // contextの破棄
  JS_DestroyContext(pContext);

  // runtimeの破棄
  JS_DestroyRuntime(pRuntime);

  return 0;
}

Linuxでのコンパイル

gcc -DXP_UNIX -o js_print js_print.cpp -lstdc++ -ljs

使い方

 $ echo "{ key:1, values:[1, 'text', false, null]}" | ./js_print