ラベル json の投稿を表示しています。 すべての投稿を表示
ラベル json の投稿を表示しています。 すべての投稿を表示

2013年5月15日水曜日

C++・de・json


int leng = Z["doc"]["info"]["inputWizard"]["subjects"].length();
これでもC++である。^^V
こーゆークラスを書いたりすると、小躍りせざるを得ない^^;^^;

2013年4月2日火曜日

PHPでサロゲートペアをJSONで扱うときのバグ。(2)

とりあえず、こういう風に修正。
【PEARのJSON.php】
function bin2hexU16($utf16){
 $ret = sprintf('\u%04s', bin2hex($utf16));
 $n = strlen($ret);
 if($n != 10) return $ret;
 return substr($ret,0,6) . '\u' . substr($ret,6);
}
//旧
//$ascii .= sprintf('\u%04s', bin2hex($utf16));
//新
$ascii .= sprintf('%s', $this->bin2hexU16($utf16));

PHPでサロゲートペアをJSONで扱うときのバグ。

どうにもこうにもダメなのである。
 何がって、PHPでサロゲートペアをJSONで扱う時のバグである。
 以下、サンプルコード。
require_once "JSON.php";
$json["doc"]["info"]["t"] = "𠀋";//0x2000Bが1文字
$jsonstr = json_encode($json);
file_put_contents("c:/tmp/2000C.txt",$jsonstr);
$jjj = new Services_JSON;
$jsonstr = $jjj->encode($json);
file_put_contents("c:/tmp/2000C-PEAR.txt",$jsonstr);
で結果なのだが、、
●json_encodeの場合
{"doc":{"info":{"t":"\ud800\udc0b"}}}
え?d800ってなんだ!?d840じゃないのか!!

●Services_JSONのencodeの場合
{"doc":{"info":{"t":"\ud840dc0b"}}}
え?d840dc0bって8桁じゃないか!?

ってことで、どっちにしてもダメです。
でも、PEARのJSONの方は、明らかに、
sprintf('\u%04s', bin2hex($utf16));
っていう行が発見できるので、8ケタの時は、\uを入れるように、ここを修正すればなんとかなる。

んーー。。やっぱこんなことやる人は、そうそういないってことだよなぁーー。

2013年3月22日金曜日

C++でJSONを扱う件(3)

C++でJSONを扱う件(3)
JSONファイルそのものをどうやって読めばいいのかって点ですが。。。
ところで話は変わって、【オブジェクト指向】ってあるじゃないですか。
よく、直感的に、思考に近い形でとか言いますよね。
CODINGは、要するに、object指向かどうかが問題なのではなくて、
頭でわかりやすいかどうかです。
単純なものほどわかりやすいのは至極当然。
よって、TREE構造のものを読み込むことを考えたときに、、
全て展開したTREEを、上から1行ずつ読み込んでいくような、そんな感じで読み込みましょう。

ポイントは、いくつかの文字の判定とその分岐です。
考えるのは、、
【"】【:】【{】【}】【[】【]】【,】
の7つだけです。
といったところで、こんな感じでつくってみました。

//wpは文字列の配列
for(int w=0;wp[w]!=0;w++){
 if(ダブルクォーテーション){
  //-----------------------------
  if(bIn){
   // ここで文字列が終わるので、文字列の特定
  }
  //-----------------------------
  else{
   // 文字列の始まりを憶えて置く
  }
  //-----------------------------
  bIn = ! bIn;
 }
 if(bIn) continue;//次へ
 else if(コロン){     // 【:】
  // キーを確定させる
  // 値の開始位置を覚える
 }
 else if(中カッコ開始){    // 【{】
  // 現在のobjectをPUSHする
  // 現在のobjectの下にobjectを生成してそれを現在のobjectにする
 }
 else if(中カッコ終了){    // 【}】
  //キーと値を調べて、少なくとも値があったら現在のobjectに登録
  //キーは無い場合もある
  //現在のobjectをひとつPOPする
 }
 else if(大カッコ開始){    // 【[】
  // 現在のobjectをPUSHする
  // 現在のobjectの下にobjectを生成してそれを現在のobjectにする
 }
 else if(大カッコ終了){    // 【]】
  //キーと値を調べて、少なくとも値があったら現在のobjectに登録
  //キーは無い場合もある
  //現在のobjectをひとつPOPする
 }
 else if(カンマ){     // 【,】
  //キーと値を調べて、少なくとも値があったら現在のobjectに登録
  //キーは無い場合もある
 }
 //-----------------------------
}
/*
 ポイント
 文字列を判断すること
 キーと値を判断すること
*/
まぁ、昔ながらのよくある方法ですが、文字列の始まりの【"】のところを憶えておき、終わりの【"】の部分に\0を突っ込んで、char*(wchar_t*)で読むっていう算段です。
値については、【"】ではじまるかどうかで、文字列かどうかを判定するわけですね。
もちろん、このLOOPに入る前に、\\と\"をなんらかの方法で退避させ、値をobjectに登録するときに戻してやりますよ。。

たぶん、つづく。。。

2013年3月21日木曜日

C++でJSONを扱う件(2)

C++でJSONを扱う件(2)

STEP2
で、object型ですが、ここでは簡略化して、以下のように考えます。


struct objson{
    objson* parent; //    親
    objson* child;  //    子
    objson* prev;   //    兄
    objson* next;   //    弟
    char*   value;  //    データ部
    char    dat[4]; //    key部
    //その他いろいろあるけど省略
    objson& operator[](const char* c);   //["abc"]とかのため
    operator char*();                    //キャストするため
    void operator=(char* cp);            //代入するため
};

これは、objectのデータ長さが一定ではないためです。
もちろん、構造体のメンバの中に、VARIANT的な値をもつという方法もありますが、ここでは面倒になるのでやりません。
よって、bool/int/doulbeとかであれば、まぁあっても8バイトなわけなので、最初から確保するという荒業もありますが、文字列だとそうはいきません。
よって、こんな風に生成してみます。
int size = sizeof(objson) + strlen(moji) + strlen(key);
char* p = new char[size];
memset(p,0,size);
objson* o = (objson*)p;


keyを設定するには、datへcopy、値を設定するには、valueのポインタを設定してからstrcpyします。
strcpy(o->dat,key);
o->value = &o->dat[strlen(key)+1];
strcpy(o->value,value);

長さを算定してメモリを確保し、それにobjson*でキャストしています。

例えば、メモリ管理を自分で書けば、メモリを連続させたりすることもできるし、不要なnew-deleteを考えなくてもすみます。

ここでは、keyになる文字列と値になる文字列の分を足して、メモリを確保しています。


生成したあとは、parent/child/prev/nextを設定して階層をつくってやれば、
o->child->child->child
なんて指定も可能になります。

つづく。。

C++でJSONを扱う件。

C++でJSONを扱う件です。
もちろん、
double pw = doc.info.pw;
のような形でアクセスできればCODINGもラクですが、
structやらclassやらを専用に準備してなきゃできないわけで、
動的には無理なわけです。

ということで、いろんなパーサとかを見てまわったのですが、
なかなかイイ感じのものがない。
『どうする?』   →つくりましょう!

『だれがやる?』 →私でしょう!
『いつやるか?』 今でしょう!











ということで、つくりました。
といっても、上記のようにはできるわけもなく、ここはPHP方式でやります。
即ち、
double pw = doc.info.pw;
ではなく、

double pw = D["doc"]["info"]["pw"];
って形です。


STEP1
STEP1は、当然ながら、object型みたいなものを定義するところから始まります。
クラスでも構造体でもかまいませんが(C++において、classとSTRUCTはほとんど同じ)、
DOM的に、親、子、兄、弟を参照するポインタをもつobjectを定義すればいいのです。
さらに、このOBJECTを生成するようなコンテナもあったほうが、あとあと便利なので、そこからつくります。
即ち、
objectContainer C;
objson& o = *C.createObjectJson();
o["doc"]["info"]["pw"] = 297.0;
みたいな使い方です。
こうしておけば、Cのデストラクタで、createされたobjectをキレイに破棄できますよね。

。。つづく。。

2013年3月1日金曜日

で結局、JSONPathのラッパークラスを書く。。(2)

●使い方サンプル
まぁ問題は、JSONPathのpathの文字列に慣れてないことくらい^^;^^;
 
 $p = new gJPath;
 $jstr = file_get_contents(******************);
 $json = json_decode($jstr,true);

 $p->setJson($json);
// $p->json = json_decode($jstr,true); //まぁこれでも同じですが。。

 $xpath = "$..name";
 $v = $p->getValueOne($xpath); // 所得

 $xpath = "$..layers.[*].name";
 $v = $p->getValueOne($xpath); // 所得

 $xpath = "$..imgurl";
 $v = $p->getValueOne($xpath); // 所得

 $xpath = "$..layers.[*].name"; // 配列として所得
 $v = $p->getValue($xpath);
 foreach($v as $one) echo "【{$one}】";//表示させてみたりとか

 $xpath = "$..layers.[*].name";
 $v = $p->getPathOne($xpath);

 $xpath = "$..layers.[*].items[?(@['name']=='札文字')]";
 $v = $p->getValueOne($xpath); // 所得
 $v = $p->getValue($xpath); // 配列として所得

 $xpath = "$..name";
 $p->setValue($xpath,"new 'Name' DA\\YO!");//一括更新

 $xpath = "$..items[?(@['name']=='札文字')]";
 $patstr = $p->getPathOne($xpath);
 $patstr .= "['mtext']";
 $v = $p->getValueDirect($patstr);  // patstrから値を所得
 $p->setValueDirect($patstr,"でへへへ!");// patstrから値を設定
 $v = $p->getValueDirect($patstr); // patstrから値を所得(当然かわっている)
●クラス
 require_once "./jsonpath-0.8.1.php";
 //---------------------------------------
class gJPath{
 public $json;   // object
 //-------------------------
 /* 初期値 */
 //-------------------------
 function __construct(){
  $this->json = null;
 }
 //-------------------------
 // objectをセットする
 //-------------------------
 public function setJson(&$v){
  $this->json = &$v; // ここで&を忘れちゃダメよ。
 }
 //-------------------------
 function __destruct(){
  // とりあえず特にやることなし。
 }
 //-------------------------
 // マッチしたものを返す。
 // falseでなければ、Arrayを返す
 //-------------------------
 public function getValue($pat){
  $match = jsonPath($this->json, $pat);
  return $match;
 }
 //-------------------------
 // マッチしたものを返す。
 // falseか値かどっちかを返す。
 // $patが必ず1コか0コが確定している場合とかに使う。
 //-------------------------
 public function getValueOne($pat){
  $match = $this->getValue($pat);
  if($match == false) return false;
  return $match[0];
 }
 //-------------------------
 // マッチしたPATHを返す。
 // falseでなければ、Arrayを返す
 //-------------------------
 public function getPath($pat){
  $match = jsonPath($this->json, $pat, array("resultType" => "PATH"));
  return $match;
 }
 //-------------------------
 // 
 //-------------------------
 public function getPathOne($pat){
  $match = $this->getPath($pat);
  if($match == false) return false;
  return $match[0];
 }
 //-------------------------
 // マッチするものに全て値を設定する。
 //-------------------------
 public function setValue($pat,$value){
  $match = $this->getPath($pat);
  if($match != false){
   foreach($match as $u){
    $this->setValueDirect($u,$value);
   }
  }
 }
 //-------------------------
 // 【$['abc'][0]】のような文字列と値を引数にしてセットする。
 // arrayとかをvalueにするなよ!
 // $pathはパターンじゃないから注意!
 //-------------------------
 public function setValueDirect($path,$value){
  $evalstr = "\x24this->json" . substr($path,1);
  $valuestr = "";
  if(is_string($value)){
   $value = preg_replace("/\x5c\x5c/", "\x5c\x5c\x5c\x5c",$value);  // 【\】 を 【\\】 にする
   $value = preg_replace("/\x27/",  "\x5c\x27",$value);    // 【'】 を 【\'】 にする
   $valuestr = "'{$value}'";
  }else{
   $valuestr = "{$value}";
  }
  @eval("{$evalstr}={$valuestr};");
 }
 //-------------------------
 // $pathはパターンじゃないから注意!
 //-------------------------
 public function getValueDirect($path){
  $evalstr = "\x24this->json" . substr($path,1);
  @eval("\x24v={$evalstr};");
  return $v;
 }
 //-------------------------

JSONPathのサイトの説明の間違い。


いやいや。。。。。
ライブラリの説明サンプルが、しっかり間違っていたのか、
それとも、仕様が変わったのにサンプルを修正していなかったのか、、
こぉーゆーことがあるとちょっとめげますが。。。。
http://goessner.net/articles/JsonPath
ということで、正誤表。
priceが10以下をselectするJSONPathですが。。。(-_-;)

誤: $..book[?(@.price<10)]

正: $..book[?(@['price']<10)]

です。

2013年2月28日木曜日

で結局、JSONPathのラッパークラスを書く。。

で結局、JSONPathのラッパークラスを書いてみた。
当然、どー使いたいかを書いてからコーディング。
★使い方のイメージ
$p = new gJPath;
$p->setJson($json);
$patstr = $p->getPathOne("$..author2"); // patString所得
$value = $p->getValueDirect($patstr);  // 所得
$p->setValueDirect($patstr,$newValue);  // 設定
こんな感じで。
できたけど、、これからテスト&デバッグ。

今さらながらJSONPath。(PHP)

なぜ今さらながらJSONPathかっていうと、どうにもいい感じにつかえるやつがなかったのです。
http://goessner.net/articles/JsonPath/
LastUpdateが2007年だけど^^;^^;
で、テストコードを書いてみると、ちょっとイイ感じです。
何がイイかって、resultTypeに"PATH"なるものが設定できるところで、この先頭の$を削除してObjectの変数名をつっこみ、
"黒魔術"のevalで、参照したり設定したり。
※javascriptの方も同様にできました。
●テストコード
 require_once "pear/Var_Dump.php";
 require_once "./jsonpath-0.8.1.php";

 $jstr = file_get_contents('samplejson.txt');
 $json = json_decode($jstr,true);

 $match1 = jsonPath($json, "$..author");
 $match2 = jsonPath($json, "$..author", array("resultType" => "PATH"));

 $res1 = Var_Dump::display($match1,true);
 $res2 = Var_Dump::display($match2,true);

 header( "Content-Type:text/html; charset=utf-8");

 $idxstr = $match2[1];
 $idxstr = "\x24json" . substr($idxstr,1);

 eval("\x24aa=" . $idxstr . ";");
 echo "->  {$aa} 
"; $idxstr .= "='A123456789';"; @eval($idxstr); // 失敗してもtry-catchで拾えません^^; $res3 = Var_Dump::display($json,true); echo "-> " . $idxstr . "
"; echo $res1; echo $res2; echo $res3; exit; /* samplejson.txtの中身。 { "store": { "book": [ { "category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95 }, { "category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99 }, { "category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "isbn": "0-553-21311-3", "price": 8.99 }, { "category": "fiction", "author": "J. R. R. Tolkien", "title": "The Lord of the Rings", "isbn": "0-395-19395-8", "price": 22.99 } ], "bicycle": { "color": "red", "price": 19.95 } } } */ ?>
●表示結果
-> Evelyn Waugh 
-> $json['store']['book'][1]['author']='A123456789';
array(4) {
  0 => string(10) Nigel Rees
  1 => string(12) Evelyn Waugh
  2 => string(15) Herman Melville
  3 => string(16) J. R. R. Tolkien
}
array(4) {
  0 => string(31) $['store']['book'][0]['author']
  1 => string(31) $['store']['book'][1]['author']
  2 => string(31) $['store']['book'][2]['author']
  3 => string(31) $['store']['book'][3]['author']
}
array(1) {
  store => array(2) {
    book => array(4) {
      0 => array(4) {
        category => string(9) reference
        author => string(10) Nigel Rees
        title => string(22) Sayings of the Century
        price => float 8.95
      }
      1 => array(4) {
        category => string(7) fiction
        author => string(10) A123456789
...略...

2013年2月27日水曜日

PHPでJSONデータを扱うとき。

PHPでJSONを扱うときの話ですが、
まぁこんなデータを扱う時の話ですが、、、
 //-------------------------------------------
{
 "doc":{
  "pages":{
   "layers":[
    {
     "items":[
      {
       "name":"abcdefgh",
       。。。。データいろいろ。。。。
       "items":[
        {
         。。理論上無限階層下。。データいろいろ。。。。
うん。
*["items > name == {$tareget}"]
セレクタみたいなやつで一発でかけるならいいんですが、、、まぁそういうクラスを書いてもいいんだけど、、、それほどでもなかったので、なめてみるコードを書いてみました
こんな感じで。
 //-------------------------------------------
 // objectのTREEを下っていって、*['name']=$nameのとき*[$tag]=$valueをセットする件。
 //-------------------------------------------
 public static function searchAndSet(&$json,$name,$tag,$value){
  $ar = array_keys($json);
  $n = count($ar);
  for($w=0;$w<$n;$w++){
   $br = @array_keys( $json[ $ar[$w]  ] );
   if(count($br) > 0){
    $ret = xxxxxxxx::searchAndSet($json[$ar[$w]],$name,$tag,$value);
    if($ret != null) return $ret;
   }
   if($json[ $ar[$w] ]['name'] == $name){
    $json[ $ar[$w] ][$tag] = $value;
    return $value;
   }
  }
  return null;
 }
追記) あ、NAMEが一意なのが前提なコードです。

2012年5月29日火曜日

javascriptでJSONをPOSTしてみてPHPで受け取る。

前回のつづきで、javascriptでObjectを生成し、
それをJSON化し、それをPHPでPOSTで受け取るテストをしてみます。
テストとしては、どうやって文字列処理をすればいいのかってことですね。
また、javascriptからPOSTする方法も含めてテストしてみます。

ってことで、こんなコードでテスト


php
$a = Var_Dump::display($_POST,true);
echo $a;
で、結果は、、
------------------POST------------------
array(1) {
  param => string(77) {\"dat1\":\"【\'】【\\\"】【\\\\】【/】【\\r】【\\n】【\\t】\"}
}

ってことは、、、
{"dat1":【'】【\"】【\\】【/】【\r】【\n】【\t】}
が
{\"dat1\":\"【\'】【\\\"】【\\\\】【/】【\\r】【\\n】【\\t】\"}
になってるってことです。
じゃぁこれをどうやって取り込むかってことですね。
まぁJSONできてるから、JSONをdecodeできる文字列に変更する必要があるわけです。
まず、これはPHPの設定になるわけですが、文字のエンコードを調べます。
echo mb_detect_encoding($param);
ってやってみると、、
UTF-8
となりました。これが「UTF-8以外でいいことなんて、とりあえず何もありませんから、PHPは全てUTF-8前提で考えます。
ってことで、var_dumpでこういう結果↓をえるためには、
array(1) {
  dat1 => string(52) ◆【'】【"】【\】【/】【
】【
                     】【 】
}
PHP側で、こういう↓文字列処理をすればいいことになります。
$param = $_POST['param'];
$param = preg_replace("/\x5c\x5c\x22/",  "\x22",$param);  //【\"】 を 【"】 にする
$param = preg_replace("/\x5c\x5c\x27/",  "\x27",$param);  //【\'】 を 【'】 にする
$param = preg_replace("/\x5c\x5c\x5c\x5c/", "\x5c\x5c",$param); //【\\】 を 【\】 にする
もちろん、preg_replaceなので配列にして渡してもいいんですが、、、
そうやったからって言って、速度が速くなるわけでもないので(実験しました)、見やすいように3行にしました。

関数としてはたいしたことない関数。
 function objectFromJsJson($v){
  $vv = preg_replace("/\x5c\x5c\x22/",  "\x22",$v);  // 【\"】 を 【"】 にする
  $vv = preg_replace("/\x5c\x5c\x27/",  "\x27",$vv);  // 【\'】 を 【'】 にする
  $vv = preg_replace("/\x5c\x5c\x5c\x5c/", "\x5c\x5c",$vv); // 【\\】 を 【\】 にする
  $json = json_decode($vv,true);
  return $json;
 }

しかしここで、PHPでencodeすると、こんな文字列になります。
//【コード】
 $str = json_encode($json);
 echo $str;
//【結果】
{"dat1":"\u25c6\u3042\u3010'\u3011\u3010\"\u3011\u3010\\\u3011\u3010\/\u3011\u3010\r\u3011\u3010\n\u3011\u3010\t\u3011"}
日本語が\u表記になります。まぁ、文字のエンコードの問題があるので、この方が扱いやすいといわれればその通りかもしれません。

2012年5月25日金曜日

JSONのencode/decodeの注意な件。

現在、JSONを、以下の4言語で使用中なのですが、
エンコード関係のまとめを一度しなきゃいけないと思ってたので、
ちょっとまとめます。
もちろん、ASとJSは、基本的にはECMAスクリプトですが、
使うライブラリの問題もしれないので、分けます。

(1)C++ → 自前処理
(2)php → json_decode/json_encode関数
(3)ActionScript → com.adobe.serialization.json.JSONにおける、encode,decode
(4)javascript → JSON.stringify/JSON.parse関数(evalは使わない)

ということで、C++の場合は、自分でやってるので、特に言及しません。

問題になりそうなのは、主に、記号類。
その中でも、
【/】【\】【'】【"】【改行】
が重要だと思うので、これらをそれぞれで入力したとき、JSONに変換したとき、POSTで受け取るときなどをまとめてみます。

●テスト1
javascriptでオブジェクトを生成して、それをJSON文字列化。


01.html

結果
で、最後の16進コードをもとに見てみると、
【’】【¥”】【¥¥】【/】【¥r】【¥n】【¥t】
である。(全角表示)
ようするに、【’】と【/】は、escapeされない。まぁそういぅ仕様だわね。。
まぁ、あたりまえの事を、一応やってみて確認することが重要なのです。
なんで、16進コードなんかで見るかっていうと、
こんな簡単なコードでも、ブラウザによってalertの表示が違うんですよね。TABとか¥rとかね。 こんな簡単な例だと間違えないけど、javascriptを動的に文字列で作り出したりしてるときに気を抜いていると、思わぬことになるのです。