https://blogs.oracle.com/nashorn/entry/flexible_json_with_nashorn_parser
最近、Hjson(Human JSON:人間向けの構成ファイル形式で、人間がやらかすエラーを減らすことができる)に出くわしました。詳しくは以下のリンクを参照してください。
Hjson, the Human JSONNashorn Parser API (JDK9) を使ってHjsonと同様の柔軟なJSON拡張をNashornでサポートできるかどうか確認しようと考えました。
http://hjson.org
JEP 236: Parser API for Nashorn以下のFlexiJSON.parse実装では、Nashorn Parser APIを使って拡張可能なflexi JSONがデータのみ(つまり、実行コードがない)であり、ことを検証し、その後このflexi JSONからオブジェクトを作成するための評価を実行します。
http://openjdk.java.net/jeps/236
FlexiJSONでできることは以下の通りです。
- 任意の場所に1行、および複数行のコメント
- 引用符で囲まれていないプロパティ名とプロパティ値
- 正規表現のリテラル値
- 末尾のカンマの省略
- Shellスタイルのコメント (#を使ったコメント)
- 複数行の文字列値(UNIXのヒアドキュメントスタイル)
FlexiJSON.parseの簡単な利用方法は以下のような感じです。/* * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Oracle nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ "use strict"; function FlexiJSON() {} // helper to locate Nashorn Parser API classes FlexiJSON.treeType = function(name) { return Java.type("jdk.nashorn.api.tree." + name); } // Nashorn Parser API classes used FlexiJSON.ArrayLiteral = FlexiJSON.treeType("ArrayLiteralTree"); FlexiJSON.ExpressionStatement = FlexiJSON.treeType("ExpressionStatementTree"); FlexiJSON.ObjectLiteral = FlexiJSON.treeType("ObjectLiteralTree"); FlexiJSON.RegExpLiteral = FlexiJSON.treeType("RegExpLiteralTree"); FlexiJSON.Literal = FlexiJSON.treeType("LiteralTree"); FlexiJSON.Parser = FlexiJSON.treeType("Parser"); FlexiJSON.SimpleTreeVisitor = FlexiJSON.treeType("SimpleTreeVisitorES5_1"); // FlexiJSON.parse API FlexiJSON.parse = function(str) { var parser = (typeof $OPTIONS == "undefined")? FlexiJSON.Parser.create() : FlexiJSON.Parser.create("-scripting"); // force the string to be an expression by putting it inside (, ) str = "(" + str + ")"; var ast = parser.parse("", str, null); // Should not happen. parse would have thrown syntax error if (!ast) { return undefined; } // allowed 'literal' values in flexi JSON function isLiteral(node) { return node instanceof FlexiJSON.ArrayLiteral || node instanceof FlexiJSON.Literal || node instanceof FlexiJSON.ObjectLiteral || node instanceof FlexiJSON.RegExpLiteral; } var visitor; ast.accept(visitor = new (Java.extend(FlexiJSON.SimpleTreeVisitor)) { lineMap: null, throwError: function(msg, node) { if (this.lineMap) { var pos = node.startPosition; var line = this.lineMap.getLineNumber(pos); var column = this.lineMap.getColumnNumber(pos); // we introduced extra '(' at start. So, adjust column number msg = msg + " @ " + line + ":" + (column - 1); } throw new TypeError(msg); }, visitLiteral: function(node, extra) { print(node.value); }, visitExpressionStatement: function(node, extra) { var expr = node.expression; if (isLiteral(expr)) { expr.accept(visitor, extra); } else { this.throwError("only literals can occur", expr); } }, visitArrayLiteral: function(node, extra) { for each (var elem in node.elements) { if (isLiteral(elem)) { elem.accept(visitor, extra); } else { this.throwError("only literal array element value allowed", elem); } } }, visitObjectLiteral: function(node, extra) { for each (var prop in node.properties) { if (prop.getter != null || prop.setter != null) { this.throwError("getter/setter property not allowed", node); } var value = prop.value; if (isLiteral(value)) { value.accept(visitor, extra); } else { this.throwError("only literal property value allowed", value); } } }, visitCompilationUnit: function(node, extra) { this.lineMap = node.lineMap; var elements = node.sourceElements; if (elements.length > 1) { this.throwError("more than one top level expression", node.sourceElements[1]); } var stat = node.sourceElements[0]; if (! (stat instanceof FlexiJSON.ExpressionStatement)) { this.throwError("only one top level expresion allowed", stat); } stat.accept(visitor, extra); }, }, null); // safe to eval given string as flexi JSON! return eval(str); }
File: fjson.js
(訳注)var obj = FlexiJSON.parse(<<EOF // this is a comment { foo: 23, bar: [ 34, 454, 54,], // inline comment here /** multi line comments are fine too! */ # shell style line comment is fine! regex: /gdfg/i, // regexp literal str: <<END Multiple line strings via nashorn -scripting mode extension as well END } EOF) print(obj.foo); print(obj.bar); print(obj.regex); print(obj.str);
当然ながら、fjson.jsの先頭で
load("flexjson.js")
としておかなければ、FlexiJSONが何者かわからないので、必ず追加しておいてください。実行結果はこんな感じです。
0 件のコメント:
コメントを投稿