IT練習ノート

IT関連で調べたこと(実際は嵌ったこと)を書いています。

JSONのVisitorPatternをやってみた

背景もどき

諸般の事情があり、JSONをVisitorパターン的に取り扱うことになりました。JSONはツリー構造になっているので、Visitorパターンが使える仕組みがあるのかとおもったのですが、org.jsonAPIにはVisitorパターンはサポートされていないようです。

下記のような話もあったようですが、進展があるようには思われませんでした。

[JSON_PROCESSING_SPEC-64] Implement visitor pattern for JSON traversal - Java.net JIRA

どちらかというと、JSONJavaオブジェクトを相互にマッピングすることのほうが、世の中的には、着目されているとおもいます。

ともあれ、jsonにてvisitorパターンを使う方法を考えてみました。

なお、ここのエントリでのjsonのライブラリはandroidで使っているjsonライブラリです。

json - platform/libcore - Git at Google

実装のアイデア

すでにあるライブラリのインターフェースにVisitorのインターフェースを加えたいわけです。ただ、Androidでの利用を考えると、リフレクション的なことはパフォーマンスが遅くなり、できません。素直に(?)Jsonオブジェクトをラップすることにしました。

やりたいことは、JSONObjectでのVisitorパターンですが、visit対象となる型がJSONObjectしかないので、型による場合わけができません。この点は、JSONObjectをValueとした時の、key側で区別をすることにします。

また、JSONArrayの場合は、Arrayの中のJSONObjectを順次visitしていくわけですが、インデッックスもインターフェースすることにします。

ネストの深さのインターフェースも考えましたが、同じキーで深さの異なるJSONObjectは、論理的にありえても、実際の業務ではなさそうな気がするのでやめました。

下記で、org.json1となっているのは、Android Studio上で、素のJavaアプリとして動かしたかったので、上述のjsonのソースをダウンロードして、パッケージ名を変えて、プロジェクトに突っ込んで言います。あくまで便宜的にやっています。(もっと上手い方法があるとはおもいますが、やりかたがわかりませんでした。)

実装1

Visitorインターフェース

package com.example.test08;

import org.json1.JSONObject;

public interface Visitor2 {
    void visit(String key, int idx, JSONObject obj);
}

Visitorの実装

とりあえずコンソールに表示するだけで、今の段階ではキーごとの振り分けはやっていません。

package com.example.test08;

import org.json1.JSONObject;

public class LogVisitor2 implements Visitor2 {
    @Override
    public void visit(String key, int idx, JSONObject obj) {
        System.out.println(key + "[" + idx +"]:" +  obj);
    }
}

使い方

jsonファイルがあるとして、それを読み込んで、JSONObjectにします。それをラップしたうえで、Visitorを使います。

    private static void visitorLog(String path) throws FileNotFoundException, JSONException {
        String content = new Scanner(new File(path)).useDelimiter("\\Z").next();
        log(content);
        JSONObject obj = new JSONObject(content);

        LogVisitor2 v = new LogVisitor2();
        new JSONVisitedWrapper2("root", 0, obj).accept(v); // (実装をサボっているため)ルートのキーとindexを渡しておく
    }

ラッパー実装

JSONObjectの中身は、キーがわかっていない場合は、keyのitereterをつかってアクセスするしか方法がありません。 また、keyに対するvalueは、下記の場合分けとしました。

  • 普通の値(String, int, ..) -> Visitの対象としない
  • JSONObject -> Visitの対象とする
  • JSONArray -> 要素がJSONObjectの場合再帰してVisitの対象とする
  • ヌル -> 対象とするかしないかはフラグで判断する

こんな感じの実装になると思います。かなり雑ですが。

package com.example.test08;

import org.json1.JSONArray;
import org.json1.JSONException;
import org.json1.JSONObject;

import java.util.Iterator;

public class JSONVisitedWrapper2 {

    private boolean isNullSkip = true; // ヌルはvisitの対象としない

    private JSONObject json;

    private String key; // 処理対象のjsonのキー(最初に与えるJSON(いわゆるルートノード)のキーは外から与える)

    private int idx; // JSONArrayのインデックス(JSONObjectの場合はゼロとする)

    public JSONVisitedWrapper2(String key, int idx, JSONObject json) {
        this.key = key;
        this.idx = idx;
        this.json = json;
    }

    public JSONVisitedWrapper2(String key, int idx, JSONObject json, boolean isNullSkip) {
        this.key = key;
        this.idx = idx;
        this.json = json;
        this.isNullSkip = isNullSkip;
    }

    public void accept(Visitor2 visitor) {
        visitor.visit(this.key, this.idx, json);
        try {
            Iterator it = json.keys();
            while (it.hasNext()) {
                String key = (String)it.next();
                if (json.isNull(key)) {
                    if (isNullSkip) {
                        continue;
                    } else {
                        visitor.visit(key, 0, null);
                        continue;
                    }
                }
                Object next = json.get(key);
                if (next instanceof JSONObject) {
                    // System.out.println("key=" + key);
                    JSONObject jsonObj = (JSONObject)next;
                    new JSONVisitedWrapper2(key, 0, jsonObj, this.isNullSkip).accept(visitor);
                } else if (next instanceof JSONArray) {
                    JSONArray jsonArr = (JSONArray)next;
                    for (int i=0; i < jsonArr.length(); i++) {
                        if (jsonArr.isNull(i)) {
                            if (isNullSkip) {
                                continue;
                            } else {
                                visitor.visit(key, i, null);
                                continue;
                            }
                        }
                        Object obj = jsonArr.get(i);
                        if (obj instanceof  JSONObject) {
                            new JSONVisitedWrapper2(key, i, (JSONObject)obj, this.isNullSkip).accept(visitor);
                        }
                    }
                }
            }
        } catch (JSONException e) {
            throw new RuntimeException(e);あ
        }
    }

}

実行例

テストコードは下記

package com.example.test08;

import org.json1.JSONException;
import org.json1.JSONObject;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class Test08_2 {

    public static void main(String[] args) throws JSONException, FileNotFoundException {
        String base = "foobar/Test08/app/data/";
        String path;
        log("http://json.org/example.html");
        path = "/Users/ogawanaoto/AndroidStudioProjects/Test08/app/data/json01.json";
        visitorLog(path);
        log("----------------");
        log("http://www.w3schools.com/json/   #1");
        path = base + "json02.json";
        visitorLog(path);
        log("----------------");
        log("http://www.w3schools.com/json/   #2");
        path = base + "json03.json";
        visitorLog(path);
        log("----------------");
        log("http://www.w3schools.com/json/   #3");
        path = base + "json04.json";
        visitorLog(path);
        log("----------------");
        log("http://www.w3schools.com/json/   #4");
        path = base + "json05.json";
        visitorLog(path);
        log("----------------");
        log("http://www.w3schools.com/json/   #5");
        path = base + "json06.json";
        visitorLog(path);

    }

    private static void visitorLog(String path) throws FileNotFoundException, JSONException {
        String content = new Scanner(new File(path)).useDelimiter("\\Z").next();
        log(content);
        JSONObject obj = new JSONObject(content);

        LogVisitor2 v = new LogVisitor2();
        new JSONVisitedWrapper2("root", 0, obj).accept(v);
    }

    private static void log(Object obj) {
        System.out.println(obj);
    }
}

実行結果は下記

/Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/bin/java -Didea.launcher.port=7535 "-Didea.launcher.bin.path=/Applications/Android Studio.app/Contents/bin" -Dfile.encoding=UTF-8 -classpath "/Users/ogawanaoto/java/adt-bundle-mac-x86_64-20140321/sdk/platforms/android-21/android.jar:/Users/ogawanaoto/java/adt-bundle-mac-x86_64-20140321/sdk/platforms/android-21/data/res:/Users/ogawanaoto/AndroidStudioProjects/Test08/app/build/intermediates/classes/debug:/Users/ogawanaoto/AndroidStudioProjects/Test08/app/build/intermediates/exploded-aar/com.android.support/appcompat-v7/21.0.3/jars/classes.jar:/Users/ogawanaoto/AndroidStudioProjects/Test08/app/build/intermediates/exploded-aar/com.android.support/appcompat-v7/21.0.3/res:/Users/ogawanaoto/AndroidStudioProjects/Test08/app/build/intermediates/exploded-aar/com.android.support/support-v4/21.0.3/jars/libs/internal_impl-21.0.3.jar:/Users/ogawanaoto/AndroidStudioProjects/Test08/app/build/intermediates/exploded-aar/com.android.support/support-v4/21.0.3/res:/Users/ogawanaoto/AndroidStudioProjects/Test08/app/build/intermediates/exploded-aar/com.android.support/support-v4/21.0.3/jars/classes.jar:/Users/ogawanaoto/java/adt-bundle-mac-x86_64-20140321/sdk/extras/android/m2repository/com/android/support/support-annotations/21.0.3/support-annotations-21.0.3.jar:/Applications/Android Studio.app/Contents/lib/idea_rt.jar" com.intellij.rt.execution.application.AppMain com.example.test08.Test08_2
http://json.org/example.html
{"employees":[
  {"firstName":"John", "lastName":"Doe"},
  {"firstName":"Anna", "lastName":"Smith"},
  {"firstName":"Peter", "lastName":"Jones"}
]}
root[0]:{"employees":[{"firstName":"John","lastName":"Doe"},{"firstName":"Anna","lastName":"Smith"},{"firstName":"Peter","lastName":"Jones"}]}
employees[0]:{"firstName":"John","lastName":"Doe"}
employees[1]:{"firstName":"Anna","lastName":"Smith"}
employees[2]:{"firstName":"Peter","lastName":"Jones"}
----------------
http://www.w3schools.com/json/   #1
{
  "glossary": {
    "title": "example glossary",
    "GlossDiv": {
      "title": "S",
      "GlossList": {
        "GlossEntry": {
          "ID": "SGML",
          "SortAs": "SGML",
          "GlossTerm": "Standard Generalized Markup Language",
          "Acronym": "SGML",
          "Abbrev": "ISO 8879:1986",
          "GlossDef": {
            "para": "A meta-markup language, used to create markup languages such as DocBook.",
            "GlossSeeAlso": ["GML", "XML"]
          },
          "GlossSee": "markup"
        }
      }
    }
  }
}
root[0]:{"glossary":{"title":"example glossary","GlossDiv":{"GlossList":{"GlossEntry":{"GlossTerm":"Standard Generalized Markup Language","GlossSee":"markup","SortAs":"SGML","GlossDef":{"para":"A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso":["GML","XML"]},"ID":"SGML","Acronym":"SGML","Abbrev":"ISO 8879:1986"}},"title":"S"}}}
glossary[0]:{"title":"example glossary","GlossDiv":{"GlossList":{"GlossEntry":{"GlossTerm":"Standard Generalized Markup Language","GlossSee":"markup","SortAs":"SGML","GlossDef":{"para":"A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso":["GML","XML"]},"ID":"SGML","Acronym":"SGML","Abbrev":"ISO 8879:1986"}},"title":"S"}}
GlossDiv[0]:{"GlossList":{"GlossEntry":{"GlossTerm":"Standard Generalized Markup Language","GlossSee":"markup","SortAs":"SGML","GlossDef":{"para":"A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso":["GML","XML"]},"ID":"SGML","Acronym":"SGML","Abbrev":"ISO 8879:1986"}},"title":"S"}
GlossList[0]:{"GlossEntry":{"GlossTerm":"Standard Generalized Markup Language","GlossSee":"markup","SortAs":"SGML","GlossDef":{"para":"A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso":["GML","XML"]},"ID":"SGML","Acronym":"SGML","Abbrev":"ISO 8879:1986"}}
GlossEntry[0]:{"GlossTerm":"Standard Generalized Markup Language","GlossSee":"markup","SortAs":"SGML","GlossDef":{"para":"A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso":["GML","XML"]},"ID":"SGML","Acronym":"SGML","Abbrev":"ISO 8879:1986"}
GlossDef[0]:{"para":"A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso":["GML","XML"]}
----------------
http://www.w3schools.com/json/   #2
{"menu": {
  "id": "file",
  "value": "File",
  "popup": {
    "menuitem": [
      {"value": "New", "onclick": "CreateNewDoc()"},
      {"value": "Open", "onclick": "OpenDoc()"},
      {"value": "Close", "onclick": "CloseDoc()"}
    ]
  }
}}
root[0]:{"menu":{"popup":{"menuitem":[{"onclick":"CreateNewDoc()","value":"New"},{"onclick":"OpenDoc()","value":"Open"},{"onclick":"CloseDoc()","value":"Close"}]},"id":"file","value":"File"}}
menu[0]:{"popup":{"menuitem":[{"onclick":"CreateNewDoc()","value":"New"},{"onclick":"OpenDoc()","value":"Open"},{"onclick":"CloseDoc()","value":"Close"}]},"id":"file","value":"File"}
popup[0]:{"menuitem":[{"onclick":"CreateNewDoc()","value":"New"},{"onclick":"OpenDoc()","value":"Open"},{"onclick":"CloseDoc()","value":"Close"}]}
menuitem[0]:{"onclick":"CreateNewDoc()","value":"New"}
menuitem[1]:{"onclick":"OpenDoc()","value":"Open"}
menuitem[2]:{"onclick":"CloseDoc()","value":"Close"}
----------------
http://www.w3schools.com/json/   #3
{"widget": {
  "debug": "on",
  "window": {
    "title": "Sample Konfabulator Widget",
    "name": "main_window",
    "width": 500,
    "height": 500
  },
  "image": {
    "src": "Images/Sun.png",
    "name": "sun1",
    "hOffset": 250,
    "vOffset": 250,
    "alignment": "center"
  },
  "text": {
    "data": "Click Here",
    "size": 36,
    "style": "bold",
    "name": "text1",
    "hOffset": 250,
    "vOffset": 100,
    "alignment": "center",
    "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
  }
}}
root[0]:{"widget":{"image":{"vOffset":250,"src":"Images\/Sun.png","name":"sun1","alignment":"center","hOffset":250},"debug":"on","window":{"name":"main_window","width":500,"title":"Sample Konfabulator Widget","height":500},"text":{"vOffset":100,"data":"Click Here","size":36,"name":"text1","style":"bold","alignment":"center","onMouseUp":"sun1.opacity = (sun1.opacity \/ 100) * 90;","hOffset":250}}}
widget[0]:{"image":{"vOffset":250,"src":"Images\/Sun.png","name":"sun1","alignment":"center","hOffset":250},"debug":"on","window":{"name":"main_window","width":500,"title":"Sample Konfabulator Widget","height":500},"text":{"vOffset":100,"data":"Click Here","size":36,"name":"text1","style":"bold","alignment":"center","onMouseUp":"sun1.opacity = (sun1.opacity \/ 100) * 90;","hOffset":250}}
image[0]:{"vOffset":250,"src":"Images\/Sun.png","name":"sun1","alignment":"center","hOffset":250}
window[0]:{"name":"main_window","width":500,"title":"Sample Konfabulator Widget","height":500}
text[0]:{"vOffset":100,"data":"Click Here","size":36,"name":"text1","style":"bold","alignment":"center","onMouseUp":"sun1.opacity = (sun1.opacity \/ 100) * 90;","hOffset":250}
----------------
http://www.w3schools.com/json/   #4
{"web-app": {
  "servlet": [
    {
      "servlet-name": "cofaxCDS",
      "servlet-class": "org.cofax.cds.CDSServlet",
      "init-param": {
        "configGlossary:installationAt": "Philadelphia, PA",
        "configGlossary:adminEmail": "ksm@pobox.com",
        "configGlossary:poweredBy": "Cofax",
        "configGlossary:poweredByIcon": "/images/cofax.gif",
        "configGlossary:staticPath": "/content/static",
        "templateProcessorClass": "org.cofax.WysiwygTemplate",
        "templateLoaderClass": "org.cofax.FilesTemplateLoader",
        "templatePath": "templates",
        "templateOverridePath": "",
        "defaultListTemplate": "listTemplate.htm",
        "defaultFileTemplate": "articleTemplate.htm",
        "useJSP": false,
        "jspListTemplate": "listTemplate.jsp",
        "jspFileTemplate": "articleTemplate.jsp",
        "cachePackageTagsTrack": 200,
        "cachePackageTagsStore": 200,
        "cachePackageTagsRefresh": 60,
        "cacheTemplatesTrack": 100,
        "cacheTemplatesStore": 50,
        "cacheTemplatesRefresh": 15,
        "cachePagesTrack": 200,
        "cachePagesStore": 100,
        "cachePagesRefresh": 10,
        "cachePagesDirtyRead": 10,
        "searchEngineListTemplate": "forSearchEnginesList.htm",
        "searchEngineFileTemplate": "forSearchEngines.htm",
        "searchEngineRobotsDb": "WEB-INF/robots.db",
        "useDataStore": true,
        "dataStoreClass": "org.cofax.SqlDataStore",
        "redirectionClass": "org.cofax.SqlRedirection",
        "dataStoreName": "cofax",
        "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
        "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
        "dataStoreUser": "sa",
        "dataStorePassword": "dataStoreTestQuery",
        "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
        "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
        "dataStoreInitConns": 10,
        "dataStoreMaxConns": 100,
        "dataStoreConnUsageLimit": 100,
        "dataStoreLogLevel": "debug",
        "maxUrlLength": 500}},
    {
      "servlet-name": "cofaxEmail",
      "servlet-class": "org.cofax.cds.EmailServlet",
      "init-param": {
        "mailHost": "mail1",
        "mailHostOverride": "mail2"}},
    {
      "servlet-name": "cofaxAdmin",
      "servlet-class": "org.cofax.cds.AdminServlet"},

    {
      "servlet-name": "fileServlet",
      "servlet-class": "org.cofax.cds.FileServlet"},
    {
      "servlet-name": "cofaxTools",
      "servlet-class": "org.cofax.cms.CofaxToolsServlet",
      "init-param": {
        "templatePath": "toolstemplates/",
        "log": 1,
        "logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
        "logMaxSize": "",
        "dataLog": 1,
        "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
        "dataLogMaxSize": "",
        "removePageCache": "/content/admin/remove?cache=pages&id=",
        "removeTemplateCache": "/content/admin/remove?cache=templates&id=",
        "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
        "lookInContext": 1,
        "adminGroupID": 4,
        "betaServer": true}}],
  "servlet-mapping": {
    "cofaxCDS": "/",
    "cofaxEmail": "/cofaxutil/aemail/*",
    "cofaxAdmin": "/admin/*",
    "fileServlet": "/static/*",
    "cofaxTools": "/tools/*"},

  "taglib": {
    "taglib-uri": "cofax.tld",
    "taglib-location": "/WEB-INF/tlds/cofax.tld"}}}
root[0]:{"web-app":{"servlet-mapping":{"cofaxAdmin":"\/admin\/*","cofaxCDS":"\/","cofaxEmail":"\/cofaxutil\/aemail\/*","fileServlet":"\/static\/*","cofaxTools":"\/tools\/*"},"taglib":{"taglib-location":"\/WEB-INF\/tlds\/cofax.tld","taglib-uri":"cofax.tld"},"servlet":[{"init-param":{"cachePackageTagsTrack":200,"redirectionClass":"org.cofax.SqlRedirection","configGlossary:staticPath":"\/content\/static","jspFileTemplate":"articleTemplate.jsp","cacheTemplatesRefresh":15,"dataStorePassword":"dataStoreTestQuery","dataStoreClass":"org.cofax.SqlDataStore","configGlossary:installationAt":"Philadelphia, PA","cacheTemplatesTrack":100,"configGlossary:poweredBy":"Cofax","searchEngineFileTemplate":"forSearchEngines.htm","defaultFileTemplate":"articleTemplate.htm","configGlossary:adminEmail":"ksm@pobox.com","templateProcessorClass":"org.cofax.WysiwygTemplate","searchEngineListTemplate":"forSearchEnginesList.htm","cachePagesStore":100,"useDataStore":true,"templateLoaderClass":"org.cofax.FilesTemplateLoader","configGlossary:poweredByIcon":"\/images\/cofax.gif","cachePagesTrack":200,"searchEngineRobotsDb":"WEB-INF\/robots.db","cachePagesDirtyRead":10,"cachePackageTagsStore":200,"cachePackageTagsRefresh":60,"dataStoreConnUsageLimit":100,"useJSP":false,"dataStoreLogLevel":"debug","dataStoreUrl":"jdbc:microsoft:sqlserver:\/\/LOCALHOST:1433;DatabaseName=goon","templatePath":"templates","cacheTemplatesStore":50,"jspListTemplate":"listTemplate.jsp","dataStoreTestQuery":"SET NOCOUNT ON;select test='test';","dataStoreMaxConns":100,"dataStoreName":"cofax","maxUrlLength":500,"templateOverridePath":"","cachePagesRefresh":10,"dataStoreDriver":"com.microsoft.jdbc.sqlserver.SQLServerDriver","dataStoreUser":"sa","dataStoreLogFile":"\/usr\/local\/tomcat\/logs\/datastore.log","defaultListTemplate":"listTemplate.htm","dataStoreInitConns":10},"servlet-name":"cofaxCDS","servlet-class":"org.cofax.cds.CDSServlet"},{"init-param":{"mailHost":"mail1","mailHostOverride":"mail2"},"servlet-name":"cofaxEmail","servlet-class":"org.cofax.cds.EmailServlet"},{"servlet-name":"cofaxAdmin","servlet-class":"org.cofax.cds.AdminServlet"},{"servlet-name":"fileServlet","servlet-class":"org.cofax.cds.FileServlet"},{"init-param":{"dataLogMaxSize":"","log":1,"logMaxSize":"","templatePath":"toolstemplates\/","fileTransferFolder":"\/usr\/local\/tomcat\/webapps\/content\/fileTransferFolder","removeTemplateCache":"\/content\/admin\/remove?cache=templates&id=","dataLogLocation":"\/usr\/local\/tomcat\/logs\/dataLog.log","lookInContext":1,"removePageCache":"\/content\/admin\/remove?cache=pages&id=","adminGroupID":4,"betaServer":true,"logLocation":"\/usr\/local\/tomcat\/logs\/CofaxTools.log","dataLog":1},"servlet-name":"cofaxTools","servlet-class":"org.cofax.cms.CofaxToolsServlet"}]}}
web-app[0]:{"servlet-mapping":{"cofaxAdmin":"\/admin\/*","cofaxCDS":"\/","cofaxEmail":"\/cofaxutil\/aemail\/*","fileServlet":"\/static\/*","cofaxTools":"\/tools\/*"},"taglib":{"taglib-location":"\/WEB-INF\/tlds\/cofax.tld","taglib-uri":"cofax.tld"},"servlet":[{"init-param":{"cachePackageTagsTrack":200,"redirectionClass":"org.cofax.SqlRedirection","configGlossary:staticPath":"\/content\/static","jspFileTemplate":"articleTemplate.jsp","cacheTemplatesRefresh":15,"dataStorePassword":"dataStoreTestQuery","dataStoreClass":"org.cofax.SqlDataStore","configGlossary:installationAt":"Philadelphia, PA","cacheTemplatesTrack":100,"configGlossary:poweredBy":"Cofax","searchEngineFileTemplate":"forSearchEngines.htm","defaultFileTemplate":"articleTemplate.htm","configGlossary:adminEmail":"ksm@pobox.com","templateProcessorClass":"org.cofax.WysiwygTemplate","searchEngineListTemplate":"forSearchEnginesList.htm","cachePagesStore":100,"useDataStore":true,"templateLoaderClass":"org.cofax.FilesTemplateLoader","configGlossary:poweredByIcon":"\/images\/cofax.gif","cachePagesTrack":200,"searchEngineRobotsDb":"WEB-INF\/robots.db","cachePagesDirtyRead":10,"cachePackageTagsStore":200,"cachePackageTagsRefresh":60,"dataStoreConnUsageLimit":100,"useJSP":false,"dataStoreLogLevel":"debug","dataStoreUrl":"jdbc:microsoft:sqlserver:\/\/LOCALHOST:1433;DatabaseName=goon","templatePath":"templates","cacheTemplatesStore":50,"jspListTemplate":"listTemplate.jsp","dataStoreTestQuery":"SET NOCOUNT ON;select test='test';","dataStoreMaxConns":100,"dataStoreName":"cofax","maxUrlLength":500,"templateOverridePath":"","cachePagesRefresh":10,"dataStoreDriver":"com.microsoft.jdbc.sqlserver.SQLServerDriver","dataStoreUser":"sa","dataStoreLogFile":"\/usr\/local\/tomcat\/logs\/datastore.log","defaultListTemplate":"listTemplate.htm","dataStoreInitConns":10},"servlet-name":"cofaxCDS","servlet-class":"org.cofax.cds.CDSServlet"},{"init-param":{"mailHost":"mail1","mailHostOverride":"mail2"},"servlet-name":"cofaxEmail","servlet-class":"org.cofax.cds.EmailServlet"},{"servlet-name":"cofaxAdmin","servlet-class":"org.cofax.cds.AdminServlet"},{"servlet-name":"fileServlet","servlet-class":"org.cofax.cds.FileServlet"},{"init-param":{"dataLogMaxSize":"","log":1,"logMaxSize":"","templatePath":"toolstemplates\/","fileTransferFolder":"\/usr\/local\/tomcat\/webapps\/content\/fileTransferFolder","removeTemplateCache":"\/content\/admin\/remove?cache=templates&id=","dataLogLocation":"\/usr\/local\/tomcat\/logs\/dataLog.log","lookInContext":1,"removePageCache":"\/content\/admin\/remove?cache=pages&id=","adminGroupID":4,"betaServer":true,"logLocation":"\/usr\/local\/tomcat\/logs\/CofaxTools.log","dataLog":1},"servlet-name":"cofaxTools","servlet-class":"org.cofax.cms.CofaxToolsServlet"}]}
servlet-mapping[0]:{"cofaxAdmin":"\/admin\/*","cofaxCDS":"\/","cofaxEmail":"\/cofaxutil\/aemail\/*","fileServlet":"\/static\/*","cofaxTools":"\/tools\/*"}
taglib[0]:{"taglib-location":"\/WEB-INF\/tlds\/cofax.tld","taglib-uri":"cofax.tld"}
servlet[0]:{"init-param":{"cachePackageTagsTrack":200,"redirectionClass":"org.cofax.SqlRedirection","configGlossary:staticPath":"\/content\/static","jspFileTemplate":"articleTemplate.jsp","cacheTemplatesRefresh":15,"dataStorePassword":"dataStoreTestQuery","dataStoreClass":"org.cofax.SqlDataStore","configGlossary:installationAt":"Philadelphia, PA","cacheTemplatesTrack":100,"configGlossary:poweredBy":"Cofax","searchEngineFileTemplate":"forSearchEngines.htm","defaultFileTemplate":"articleTemplate.htm","configGlossary:adminEmail":"ksm@pobox.com","templateProcessorClass":"org.cofax.WysiwygTemplate","searchEngineListTemplate":"forSearchEnginesList.htm","cachePagesStore":100,"useDataStore":true,"templateLoaderClass":"org.cofax.FilesTemplateLoader","configGlossary:poweredByIcon":"\/images\/cofax.gif","cachePagesTrack":200,"searchEngineRobotsDb":"WEB-INF\/robots.db","cachePagesDirtyRead":10,"cachePackageTagsStore":200,"cachePackageTagsRefresh":60,"dataStoreConnUsageLimit":100,"useJSP":false,"dataStoreLogLevel":"debug","dataStoreUrl":"jdbc:microsoft:sqlserver:\/\/LOCALHOST:1433;DatabaseName=goon","templatePath":"templates","cacheTemplatesStore":50,"jspListTemplate":"listTemplate.jsp","dataStoreTestQuery":"SET NOCOUNT ON;select test='test';","dataStoreMaxConns":100,"dataStoreName":"cofax","maxUrlLength":500,"templateOverridePath":"","cachePagesRefresh":10,"dataStoreDriver":"com.microsoft.jdbc.sqlserver.SQLServerDriver","dataStoreUser":"sa","dataStoreLogFile":"\/usr\/local\/tomcat\/logs\/datastore.log","defaultListTemplate":"listTemplate.htm","dataStoreInitConns":10},"servlet-name":"cofaxCDS","servlet-class":"org.cofax.cds.CDSServlet"}
init-param[0]:{"cachePackageTagsTrack":200,"redirectionClass":"org.cofax.SqlRedirection","configGlossary:staticPath":"\/content\/static","jspFileTemplate":"articleTemplate.jsp","cacheTemplatesRefresh":15,"dataStorePassword":"dataStoreTestQuery","dataStoreClass":"org.cofax.SqlDataStore","configGlossary:installationAt":"Philadelphia, PA","cacheTemplatesTrack":100,"configGlossary:poweredBy":"Cofax","searchEngineFileTemplate":"forSearchEngines.htm","defaultFileTemplate":"articleTemplate.htm","configGlossary:adminEmail":"ksm@pobox.com","templateProcessorClass":"org.cofax.WysiwygTemplate","searchEngineListTemplate":"forSearchEnginesList.htm","cachePagesStore":100,"useDataStore":true,"templateLoaderClass":"org.cofax.FilesTemplateLoader","configGlossary:poweredByIcon":"\/images\/cofax.gif","cachePagesTrack":200,"searchEngineRobotsDb":"WEB-INF\/robots.db","cachePagesDirtyRead":10,"cachePackageTagsStore":200,"cachePackageTagsRefresh":60,"dataStoreConnUsageLimit":100,"useJSP":false,"dataStoreLogLevel":"debug","dataStoreUrl":"jdbc:microsoft:sqlserver:\/\/LOCALHOST:1433;DatabaseName=goon","templatePath":"templates","cacheTemplatesStore":50,"jspListTemplate":"listTemplate.jsp","dataStoreTestQuery":"SET NOCOUNT ON;select test='test';","dataStoreMaxConns":100,"dataStoreName":"cofax","maxUrlLength":500,"templateOverridePath":"","cachePagesRefresh":10,"dataStoreDriver":"com.microsoft.jdbc.sqlserver.SQLServerDriver","dataStoreUser":"sa","dataStoreLogFile":"\/usr\/local\/tomcat\/logs\/datastore.log","defaultListTemplate":"listTemplate.htm","dataStoreInitConns":10}
servlet[1]:{"init-param":{"mailHost":"mail1","mailHostOverride":"mail2"},"servlet-name":"cofaxEmail","servlet-class":"org.cofax.cds.EmailServlet"}
init-param[0]:{"mailHost":"mail1","mailHostOverride":"mail2"}
servlet[2]:{"servlet-name":"cofaxAdmin","servlet-class":"org.cofax.cds.AdminServlet"}
servlet[3]:{"servlet-name":"fileServlet","servlet-class":"org.cofax.cds.FileServlet"}
servlet[4]:{"init-param":{"dataLogMaxSize":"","log":1,"logMaxSize":"","templatePath":"toolstemplates\/","fileTransferFolder":"\/usr\/local\/tomcat\/webapps\/content\/fileTransferFolder","removeTemplateCache":"\/content\/admin\/remove?cache=templates&id=","dataLogLocation":"\/usr\/local\/tomcat\/logs\/dataLog.log","lookInContext":1,"removePageCache":"\/content\/admin\/remove?cache=pages&id=","adminGroupID":4,"betaServer":true,"logLocation":"\/usr\/local\/tomcat\/logs\/CofaxTools.log","dataLog":1},"servlet-name":"cofaxTools","servlet-class":"org.cofax.cms.CofaxToolsServlet"}
init-param[0]:{"dataLogMaxSize":"","log":1,"logMaxSize":"","templatePath":"toolstemplates\/","fileTransferFolder":"\/usr\/local\/tomcat\/webapps\/content\/fileTransferFolder","removeTemplateCache":"\/content\/admin\/remove?cache=templates&id=","dataLogLocation":"\/usr\/local\/tomcat\/logs\/dataLog.log","lookInContext":1,"removePageCache":"\/content\/admin\/remove?cache=pages&id=","adminGroupID":4,"betaServer":true,"logLocation":"\/usr\/local\/tomcat\/logs\/CofaxTools.log","dataLog":1}
----------------
http://www.w3schools.com/json/   #5
{"menu": {
  "header": "SVG Viewer",
  "items": [
    {"id": "Open"},
    {"id": "OpenNew", "label": "Open New"},
    null,
    {"id": "ZoomIn", "label": "Zoom In"},
    {"id": "ZoomOut", "label": "Zoom Out"},
    {"id": "OriginalView", "label": "Original View"},
    null,
    {"id": "Quality"},
    {"id": "Pause"},
    {"id": "Mute"},
    null,
    {"id": "Find", "label": "Find..."},
    {"id": "FindAgain", "label": "Find Again"},
    {"id": "Copy"},
    {"id": "CopyAgain", "label": "Copy Again"},
    {"id": "CopySVG", "label": "Copy SVG"},
    {"id": "ViewSVG", "label": "View SVG"},
    {"id": "ViewSource", "label": "View Source"},
    {"id": "SaveAs", "label": "Save As"},
    null,
    {"id": "Help"},
    {"id": "About", "label": "About Adobe CVG Viewer..."}
  ]
}}
root[0]:{"menu":{"header":"SVG Viewer","items":[{"id":"Open"},{"id":"OpenNew","label":"Open New"},null,{"id":"ZoomIn","label":"Zoom In"},{"id":"ZoomOut","label":"Zoom Out"},{"id":"OriginalView","label":"Original View"},null,{"id":"Quality"},{"id":"Pause"},{"id":"Mute"},null,{"id":"Find","label":"Find..."},{"id":"FindAgain","label":"Find Again"},{"id":"Copy"},{"id":"CopyAgain","label":"Copy Again"},{"id":"CopySVG","label":"Copy SVG"},{"id":"ViewSVG","label":"View SVG"},{"id":"ViewSource","label":"View Source"},{"id":"SaveAs","label":"Save As"},null,{"id":"Help"},{"id":"About","label":"About Adobe CVG Viewer..."}]}}
menu[0]:{"header":"SVG Viewer","items":[{"id":"Open"},{"id":"OpenNew","label":"Open New"},null,{"id":"ZoomIn","label":"Zoom In"},{"id":"ZoomOut","label":"Zoom Out"},{"id":"OriginalView","label":"Original View"},null,{"id":"Quality"},{"id":"Pause"},{"id":"Mute"},null,{"id":"Find","label":"Find..."},{"id":"FindAgain","label":"Find Again"},{"id":"Copy"},{"id":"CopyAgain","label":"Copy Again"},{"id":"CopySVG","label":"Copy SVG"},{"id":"ViewSVG","label":"View SVG"},{"id":"ViewSource","label":"View Source"},{"id":"SaveAs","label":"Save As"},null,{"id":"Help"},{"id":"About","label":"About Adobe CVG Viewer..."}]}
items[0]:{"id":"Open"}
items[1]:{"id":"OpenNew","label":"Open New"}
items[3]:{"id":"ZoomIn","label":"Zoom In"}
items[4]:{"id":"ZoomOut","label":"Zoom Out"}
items[5]:{"id":"OriginalView","label":"Original View"}
items[7]:{"id":"Quality"}
items[8]:{"id":"Pause"}
items[9]:{"id":"Mute"}
items[11]:{"id":"Find","label":"Find..."}
items[12]:{"id":"FindAgain","label":"Find Again"}
items[13]:{"id":"Copy"}
items[14]:{"id":"CopyAgain","label":"Copy Again"}
items[15]:{"id":"CopySVG","label":"Copy SVG"}
items[16]:{"id":"ViewSVG","label":"View SVG"}
items[17]:{"id":"ViewSource","label":"View Source"}
items[18]:{"id":"SaveAs","label":"Save As"}
items[20]:{"id":"Help"}
items[21]:{"id":"About","label":"About Adobe CVG Viewer..."}

Process finished with exit code 0

実装2

実際に使う時は下記のように業務に合わせてJSONObjectの場合わけをすることになると思います。

package com.example.test08;

import org.json1.JSONObject;

public abstract class LogVisitor2_Servlet implements Visitor2 {

    @Override
    public void visit(String key, int idx, JSONObject obj) {
        if ("root".equals(key)) {
            visitRoot(idx, obj);
        } else if ("web-app".equals(key)) {
            visitWebApp(idx, obj);
        }else if ("servlet".equals(key)) {
            visitServe(idx, obj);
        }else if ("servlet-mapping".equals(key)) {
            visitServeltMapping(idx, obj);
        }else if ("taglib".equals(key)) {
            visitTagLib(idx, obj);
        }else if ("init-param".equals(key)) {
            visitInitParam(idx, obj);
        }
    }

    abstract void visitRoot(int idx, JSONObject obj);

    abstract void visitWebApp(int idx, JSONObject obj) ;

    abstract void visitServe(int idx, JSONObject obj);

    abstract void visitServeltMapping(int idx, JSONObject obj);

    abstract void visitTagLib(int idx, JSONObject obj);

    abstract void visitInitParam(int idx, JSONObject obj);

}

所感

  • Visito対象とするノードの情報を外から与えるほうがよいと思う。ここでの実装は効率的ではない。
  • ここでの実装は真偽値の対応ができていない。
  • Nullの取り扱いが面倒。JSONではヌルは明示的にテキスト上nullという表現になる。
  • 時間があったらテストしたい。いろいろ直したい。