使用Java将JSON文件转换为RDF格式

时间:2017-06-26 04:51:34

标签: java json rdf

这里我想将JSON文件转换为RDF。这是JSON

{
"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"
            }
        }
    }
}
}

我找不到将其转换为RDF的正确方法。

8 个答案:

答案 0 :(得分:7)

没有标准的方法将JSON解释为RDF。有几种方法可以从JSON文件生成RDF(用Java或其他方式)。您可以简单地使用在Java中实现的JSON解析器,然后提取相关部分并使用RDF的Java库构建RDF图,例如Apache JenaRDF4J(以前称为 Sesame )。但是,还有其他方法可以使任务更容易:

  • 通过向其添加@context将JSON文件转换为JSON-LD文件。这适用于简单的案例,但不足以涵盖许多相关案例。
  • 使用RML,一种用于表示从各种数据格式(包括JSON)到RDF的映射的语言。它有一个Java参考实现。 RML是R2RML的扩展,因此它也可以将关系数据映射到RDF,如果您熟悉R2RML,则理解RML的工作方式相对容易。还有a graphical editor,但似乎无法下载。
  • 使用SPARQL-Generate,一种表示从非RDF数据源(包括JSON)到RDF的映射的语言。它有一个基于Jena的参考实现。它扩展了SPARQL,因此如果您熟悉SPARQL,它应该很容易使用它。它可以是tested online

免责声明:我为SPARQL-Generate做出了贡献。

答案 1 :(得分:3)

如果你的目标是简单地获得有效的RDF而不做任何关于结构的决定,你可以简单地添加一个@context对象并将现有的JSON转换为JSON-LD,例如。

{
  "@context": {"@vocab": "http://example.org/ontology#"},
  "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"
            }
        }
    }
  }
}

然后可以使用适当的RDF / JSON-LD库将其解释为RDF,RDF结构将是:

@prefix ns0: <http://example.org/ontology#> .

[] ns0:glossary [
    ns0:GlossDiv [
      ns0:GlossList [ ns0:GlossEntry [
          ns0:Abbrev "ISO 8879:1986" ;
          ns0:Acronym "SGML" ;
          ns0:GlossDef [
            ns0:GlossSeeAlso "GML", "XML" ;
            ns0:para "A meta-markup language, used to create markup languages such as DocBook."
          ] ;
          ns0:GlossSee "markup" ;
          ns0:GlossTerm "Standard Generalized Markup Language" ;
          ns0:ID "SGML" ;
          ns0:SortAs "SGML"
        ] ] ;
      ns0:title "S"
    ] ;
    ns0:title "example glossary"
  ] .

这可能是奇怪的RDF,但它可以使用RDF工具加载和操作。

您可以使用json-ld playground

中的示例

答案 2 :(得分:3)

您可以使用RML将JSON文件转换为RDF。 为此,您需要首先创建一些映射规则。 RML处理器使用这些映射规则来转换您的输入数据 进入RDF。 RML处理器的一个示例是 RML mapper

  1. 创建RML LogicalSource。 这描述了RML处理器应如何以及在何处访问输入数据:
<#LogicalSource>
    a rml:logicalSource;
    rml:source "data.json";
    rml:referenceFormulation ql:JSONPath;
    rml:iterator "$.glossary".

此RDF代码段告诉RML处理器他必须检索data.json并 使用JSONPath表达式(ql:JSONPath)遍历数据 由rml:iterator指定。

  1. 创建一个TriplesMap。这提供 有关如何将输入数据转换为RML处理器的信息 RDF。提供的数据示例如下:
<#GlossaryMapping>
    a rr:TriplesMap;
    rml:logicalSource <#LogicalSource>;

    rr:subjectMap [
        rr:template "http://example.org/glossary/{title}";
    ];

    rr:predicateObjectMap [
        rr:predicate ex:title;
        rr:objectMap [
            rml:reference "title";
        ];
    ].

TriplesMap使用创建的LogicalSource访问数据。三元组地图 将确保RML处理器使用 http://example.org/glossary/{title}作为主语,ex:title作为谓语, JSON属性title作为对象的值

  1. TriplesMaps也可以彼此链接。 如果您有一个列表(GlossEntry)的条目输入到更高的元素glossary 例如,您可以编写以下映射规则:
<#GlossaryMapping>
    a rr:TriplesMap;
    rml:logicalSource <#LogicalSource>;

    rr:subjectMap [
        rr:template "http://example.org/glossary/{title}";
    ];

rr:predicateObjectMap [
        rr:predicate ex:glossDiv;
        rr:objectMap [
            rr:parentTriplesMap <#GlossListEntryMapping>;
            rr:child "GlossDiv.title";
            rr:parent "GlossDiv.title";
        ];
    ].

<#GlossListEntryMapping>
    a rr:TriplesMap;
    rml:logicalSource <#LogicalSource>;

    rr:subjectMap [
        rr:template "http://example.org/entry/{GlossDiv.title}";
    ];

TripleMaps都将在考虑到 rr:joinCondition。如果没有提供加入条件,则孩子的每个科目 将会与家长的每个科目一起加入。

映射规则

在此完整示例中,我省略了一些JSON属性以使其简洁。

@base <http://example.org> .
@prefix rr: <http://www.w3.org/ns/r2rml#> .
@prefix rml: <http://semweb.mmlab.be/ns/rml#> .
@prefix ql: <http://semweb.mmlab.be/ns/ql#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix ex: <http://example.org/ns#> .

<#LogicalSource>
    a rml:logicalSource;
    rml:source "data.json";
    rml:referenceFormulation ql:JSONPath;
    rml:iterator "$.glossary".

<#GlossaryMapping>
    a rr:TriplesMap;
    rml:logicalSource <#LogicalSource>;

    rr:subjectMap [
        rr:template "http://example.org/glossary/{title}";
    ];

    rr:predicateObjectMap [
        rr:predicate ex:title;
        rr:objectMap [
            rml:reference "title";
        ];
    ];

    rr:predicateObjectMap [
        rr:predicate ex:glossDiv;
        rr:objectMap [
            rr:parentTriplesMap <#GlossListEntryMapping>;
            rr:child "GlossDiv.title";
            rr:parent "GlossDiv.title";
        ];
    ].

<#GlossListEntryMapping>
    a rr:TriplesMap;
    rml:logicalSource <#LogicalSource>;

    rr:subjectMap [
        rr:template "http://example.org/entry/{GlossDiv.title}";
    ];

    rr:predicateObjectMap [
        rr:predicate ex:ID;
        rr:objectMap [
            rml:reference "GlossDiv.GlossList.GlossEntry.ID" 
        ];
    ];

    rr:predicateObjectMap [
        rr:predicate ex:Abbrev;
        rr:objectMap [
            rml:reference "GlossDiv.GlossList.GlossEntry.Abbrev" 
        ];
    ];

    # Other properties can be mapped too if needed

    rr:predicateObjectMap [
        rr:predicate ex:glossSeeAlso;
        rr:objectMap [ # Mapping arrays can also be done
            rml:reference "GlossDiv.GlossList.GlossEntry.GlossDef.GlossSeeAlso.[*]";
        ];
    ].

输出

<http://example.org/entry/S> <http://example.org/ns#glossSeeAlso> "GML".
<http://example.org/entry/S> <http://example.org/ns#glossSeeAlso> "XML".
<http://example.org/entry/S> <http://example.org/ns#ID> "SGML".
<http://example.org/entry/S> <http://example.org/ns#Abbrev> "ISO 8879:1986".
<http://example.org/glossary/example%20glossary> <http://example.org/ns#title> "example glossary".
<http://example.org/glossary/example%20glossary> <http://example.org/ns#glossDiv> <http://example.org/entry/S>.

注意:我为RML及其技术做出了贡献。

答案 3 :(得分:1)

如果我理解你的问题,我建议使用Apache的开源语义网库Apache Jena

这是RDF creation tutorial

我还发现了JSON2RDF,这个项目声称利用Jena创建了一种将JSON转换为RDF的语言,但我自己没有测试过,而且文档含糊不清。如果您最终查看它,请分享您的经验。

答案 4 :(得分:0)

我们已经发布了开源JSON2RDF转换器(不要与vaiden提到的JSON2RDF混淆),它可以使您做到这一点: https://github.com/AtomGraph/JSON2RDF

JSON2RDF + SPARQL可以用作JSON-LD + "S-Port" : "62416", "CS-Username" : "-", "CS-URI-Query" : "-" 的更灵活选择。

答案 5 :(得分:0)

我们正在提供JSON到JSONLD转换服务:https://json2ld.mapper.tokyo/

首先,您粘贴或上传JSON数据,然后立即生成其JSONLD版本。

如果JSON数据中有匹配的术语(例如“名称”)被映射到https://schema.org/name,它将自动映射一些现有词汇,例如schema.org。

您可以编辑要映射的词汇或术语,并从GitHub存储库中发布上下文。

答案 6 :(得分:0)

我最近花了一些时间将一些旧的 java 代码粘贴在一起,以创建一个名为“oi”的命令行工具。

https://github.com/jschnasse/oi

致电

oi stack44753298.json -t turtle

印刷品

_:b0 <info:oi/glossary> _:b1 .

_:b1 <info:oi/GlossDiv> _:b2 .

_:b2 <info:oi/GlossList> _:b3 .

_:b3 <info:oi/GlossEntry> _:b4 .

_:b4 <info:oi/Abbrev> "ISO 8879:1986";
  <info:oi/Acronym> "SGML";
  <info:oi/GlossDef> _:b5 .

_:b5 <info:oi/GlossSeeAlso> "GML", "XML";
  <info:oi/para> "A meta-markup language, used to create markup languages such as DocBook." .

_:b4 <info:oi/GlossSee> "markup";
  <info:oi/GlossTerm> "Standard Generalized Markup Language";
  <info:oi/ID> "SGML";
  <info:oi/SortAs> "SGML" .

_:b2 <info:oi/title> "S" .

_:b1 <info:oi/title> "example glossary" .

该工具使用非常粗略的方法将 rdf 转换为 json,反之亦然。对于 json 到 rdf 的情况,它仅通过将所有 json 键放入 info:namespace 来创建临时上下文。之后,它使用上下文将 json 读取为 jsonld。与本主题 by brinxmat 中已经描述的完全一样。

它大致遵循我在最近的 stackoverflow 线程中写的想法:

答案 7 :(得分:-1)

使用此JSON解析器将JSON读取到Java对象,并使用之前读取的Java对象创建rdf。

package your_package;

import java.io.ByteArrayInputStream;  

import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream;  
import java.io.PrintStream;
import java.nio.charset.Charset; 
import java.util.AbstractList;  
import java.util.ArrayList;  
import java.util.Collection;
import java.util.HashMap; 
import java.util.Iterator;  
import java.util.List; 
import java.util.Map;

<html>


 /**
     * JSON is an open standard format that uses human-readable text to transmit
     * Data objects consisting of attribute–value pairs. It is used primarily to
     * Transmit data between a server and web application, as an alternative to XML.
     *  
     * JSON's basic types are:
     * <ul>
     * <li><b>Number</b>: a signed decimal number that may contain a fractional part
     * And may use exponential E notation. JSON does not allow non-numbers like NaN,
     * Nor does it make any distinction between integer and floating-point. (Even
     * Though JavaScript uses a double-precision floating-point format for all its
     * Numeric values, other languages implementing JSON may encode numbers
     * differently) </li>
     * <li><b>String</b>: a sequence of zero or more Unicode characters. Strings are
     * Delimited with double-quotation marks and support a backslash escaping
     * syntax.</li>
     * <li><b>Boolean</b>: either of the values {@code true} or {@code false}</li>
     * <li><b>Array</b>: an ordered list of zero or more values, each of which may
     * Be of any type. Arrays use square bracket notation with elements being
     * comma-separated.</li>
     * <li><b>Object</b>: an unordered collection of name/value pairs where the
     * Names (also called keys) are strings. Since objects are intended to represent
     * Associative arrays, it is recommended, though not required, that each key is
     * Unique within an object. Objects are delimited with curly brackets and use
     * Commas to separate each pair, while within each pair the colon {@code ':'}
     * Character separates the key or name from its value.</li>
     * <li><b>null</b>: An empty value, using the word null</li>
     * </ul>
     * Conversion table:
     * <table border='1'>
     * <tr><td>java.util.Map</td><td>JSON Object {"key":[0,2,3]}</td></tr>
     * <tr><td>java.util.Collection</td><td>JSON Array [0,1,2,"string"]</td></tr>
     * <tr><td>java.lang.String</td><td>JSON String "example"</td></tr>
     * <tr><td>java.lang.Boolean</td><td>JSON Boolean (true/false) </td></tr>
     * <tr><td>java.lang.Number</td><td>JSON Number (-2.5E2) </td></tr>
     * <tr><td>null</td><td>JSON Null (null) </td></tr>
     * </table>
     * Any other object will be write with the next formula:  
     * <u>{@code obj.toString()}</u>. For example:
     * {@Code write (out, new java.lang.object())}. The output stream will be
     * Contains the hashcode of newly created object because the default object
     * toString() method returns the object hashcode. This class supported the
     * Duplicated keys in the object map.
     *  
     * The JavaScript Object Notation (JSON) Data Interchange Format:
     * <A href='http://tools.ietf.org/html/rfc7159'>RFC-7159</a>
     *
     * @See DuplicatedKeyList
     */
public class Json {
    /**
     * This field represents when the json object is finished, no more available
     * data to processing.
     */
    private static final Object close = new Object();
    /**
     * The step offsets in the rows.
     */
    private static final int writeSpaceCount = 1;
    /**
     * 'n' 'u' 'l' 'l'
     */
    public static final byte[] NULL = new byte[]{'n', 'u', 'l', 'l'};
    /**
     * The null identifer, also called: the first character of null.
     */
    public static final int NULL_LOWER = 'n';
    /**
     * The null identifer, also called: the first character of null (uppercase).
     */
    public static final int NULL_UPPER = 'N';
    /**
     * The first character of {@code true}.
     */
    public static final int TRUE = 't';
    /**
     * The first character of {@code true} (uppercase).
     */
    public static final int TRUE_UPPER = 'T';
    /**
     * The first character of {@code false}.
     */
    public static final int FALSE = 'f';
    /**
     * The first character of {@code false} (uppercase).
     */
    public static final int FALSE_UPPER = 'F';
    /**
     * Colon ascii value
     */
    public static final int COLON = ':';
    /**
     * Comma ascii value
     */
    public static final int COMMA = ',';
    /**
     * left bracket (the list identifier, first character)
     */
    public static final int LEFT_BRACKET = '[';
    /**
     * left brace (the map identifier, first character)
     */
    public static final int LEFT_BRACE = '{';
    /**
     * right bracket (the list identifier, last character)
     */
    public static final int RIGHT_BRACKET = ']';
    /**
     * right bracket (the map identifier, last character)
     */
    public static final int RIGHT_BRACE = '}';
    /**
     * the string identifier: {@code "}
     */
    public static final int STRING = '"';
    /**
     * the space ascii value
     */
    public static final int SP = ' ';
    /**
     * the backslash ascii value
     */
    public static final int BS = '\\';
    /**
     * the CR (carriage return) ascii value
     */
    public static final int CR = 13;
    /**
     * the line feed ascii value
     */
    public static final int LF = 10;
    /**
     * the end of file identifier
     */
    public static final int EOF = -1;
    /**
     * end of line identifier (CR + LF)
     */
    public static final byte[] EOL = new byte[]{CR, LF};

    /**
     * the byte array buffer to read
     */
    private final ByteArrayOutputStream buf = new ByteArrayOutputStream();

    /**
     * Creates a new JSON which can read and write json objects.
     */
    public Json() {
    }

    /**
     * Creates a new empty map.
     *
     * @return a new empty map.
     */
    private Map createEmptyMap() {
        return new HashMap();
    }

    /**
     * Writes a specified object {@code obj} to a specified output stream
     * {@code out}, also called: creates a json document from the specified
     * object. Duplicated keys are supported. Conversion table: see JSON class
     * javadoc.
     *
     * @param out the specified output stream
     * @param obj the specified object
     * @throws IOException If IO Error Occurs.
     * @see Json
     */
    public void write(PrintStream out, Object obj) throws IOException {
        write(out, obj, false);
    }

    /**
     * Writes a specified object {@code obj} to a specified print stream
     * {@code out}, also called: creates a json document from the specified
     * object. Duplicated keys are supported. Conversion table: see JSON class
     * javadoc.
     *
     * @param out the specified print stream
     * @param obj the specified object
     * @param format {@code true} if the output is formatted, otherwise
     * {@code false}.
     * @throws IOException If IO Error Occurs.
     * @see Json
     * @see DuplicatedKeyList
     */
    public void write(PrintStream out, Object obj, boolean format) throws IOException {
        write(out, obj, format, 0);
    }

    /**
     * Writes a specified object {@code obj} to a specified print stream
     * {@code out}, also called: creates a json document from the specified
     * object. Duplicated keys are supported. Conversion table: see JSON class
     * javadoc.
     *
     * @param out the specified print stream
     * @param obj the specified object
     * @param format {@code true} if the output is formatted, otherwise
     * {@code false}.
     * @param charset the charset which represents the json document encodings
     * @param depth the current depth from the root element
     * @throws IOException If IO Error Occurs.
     * @see Json
     * @see DuplicatedKeyList
     */
    private synchronized void write(PrintStream out, Object obj, boolean format, int depth) throws IOException {
        if (obj == null) {
            out.write(NULL);
            out.flush();
            return;
        }
        if (obj instanceof String) {
            out.write(STRING);
            out.print(escape((String) obj));
            out.write(STRING);
            out.flush();
            return;
        } else if (obj instanceof Map) {
            out.write(LEFT_BRACE);
            Map map = (Map) obj;
            Iterator<Map.Entry> it = map.entrySet().iterator();
            Map.Entry entry;
            while (it.hasNext()) {
                entry = it.next();
                String key = escape(entry.getKey().toString());
                Object val = entry.getValue();
                if (val instanceof DuplicatedKeyList) {
                    writeMulti(out, key, (List) val, format, depth);
                } else {
                    if (format) {
                        writeBreak(out, depth + writeSpaceCount);
                    }
                    write(out, key, format, depth + writeSpaceCount);
                    out.write(COLON);
                    if (format) {
                        writeSpace(out, writeSpaceCount);
                    }
                    write(out, val, format, depth + writeSpaceCount);
                }
                if (it.hasNext()) {
                    out.write(COMMA);
                }
            }
            if (format) {
                writeBreak(out, depth);
            }
            out.write(RIGHT_BRACE);
            out.flush();
            return;
        } else if (obj instanceof Collection) {
            out.write(LEFT_BRACKET);
            Iterator it = ((Collection) obj).iterator();
            while (it.hasNext()) {
                if (format) {
                    writeBreak(out, depth + writeSpaceCount);
                }
                write(out, it.next(), format, depth + writeSpaceCount);
                if (it.hasNext()) {
                    out.write(COMMA);
                }
            }
            if (format) {
                writeBreak(out, depth);
            }
            out.write(RIGHT_BRACKET);
            out.flush();
            return;
        }
        if (obj instanceof Number || obj instanceof Boolean) {
            out.print(obj);
        } else {
            out.write(STRING);
            out.print(escape(obj.toString()));
            out.write(STRING);
        }
        out.flush();
    }

    /**
     * Reads a specified input stream {@code in} which contains json elements
     * and returns the java object representation of json elements. Conversion
     * table: see JSON class javadoc.
     *
     * @param in the specified input stream
     * @return the java object representation of json elements.
     * @throws IOException If IO Error Occurs.
     * @see Json
     * @see DuplicatedKeyList
     */
    public Object read(InputStream in) throws IOException {
        return read(in, Charset.forName("UTF-8"));
    }

    /**
     * Reads a specified input stream {@code in} which contains json elements
     * and returns the java object representation of json elements. Conversion
     * table: see JSON class javadoc.
     *
     * @param in the specified input stream
     * @param charset the json document encodings
     * @return the java object representation of json elements.
     * @throws IOException If IO Error Occurs.
     * @see Json
     * @see DuplicatedKeyList
     */
    public synchronized Object read(InputStream in, Charset charset) throws IOException {
        int b;
        while ((b = in.read()) != EOF) {
            if (b > 32 && b != COMMA) {
                switch (b) {
                    //list
                    case LEFT_BRACKET: {
                        List list = new ArrayList();
                        Object obj;
                        while ((obj = read(in, charset)) != close) {
                            if (obj instanceof Finish) {
                                list.add(((Finish) obj).val);
                                break;
                            } else {
                                list.add(obj);
                            }
                        }
                        return list;
                    }
                    //map
                    case LEFT_BRACE: {
                        Map map = createEmptyMap();
                        Object key;
                        Object val;
                        while ((key = read(in, charset)) != close) {
                            while ((b = in.read()) != COLON) {
                                if (b == EOF) {
                                    throw new IOException("EOF");
                                }
                            }
                            val = read(in, charset);
                            if (map.containsKey(key)) {
                                Object prev = map.get(key);
                                DuplicatedKeyList list;
                                if (prev instanceof DuplicatedKeyList) {
                                    list = (DuplicatedKeyList) prev;
                                    //((DuplicatedKeyList) prev).add(val);
                                } else {
                                    list = new DuplicatedKeyList(new ArrayList());
                                    list.add(prev);
                                }
                                list.add(val);
                                map.put(key, list);
                                //}
                                System.err.println("WARNING: duplicated key: " + key);
                            } else {
                                if (val instanceof Finish) {
                                    val = ((Finish) val).val;
                                    map.put(key, val);
                                    break;
                                } else {
                                    map.put(key, val);
                                }
                            }
                        }
                        return map;
                    }
                    //string
                    case STRING: {
                        buf.reset();
                        int a = 0;
                        while ((b = in.read()) != STRING || a == BS) {
                            buf.write(b);
                            a = b;
                        }
                        return unescape(buf.toString(charset.name()));
                    }
                    case TRUE_UPPER: {
                    }
                    //true
                    case TRUE: {
                        in.skip(4);
                        return true;
                    }
                    //false
                    case FALSE_UPPER: {
                    }
                    case FALSE: {
                        in.skip(5);
                        return false;
                    }
                    //null
                    case NULL_UPPER: {
                    }
                    case NULL_LOWER: {
                        in.skip(4);
                        return null;
                    }
                    //map right brackets
                    case RIGHT_BRACE: {
                    }
                    case RIGHT_BRACKET: {
                        return close;
                    }
                    //number
                    default: {
                        buf.reset();
                        buf.write(b);
                        while ((b = in.read()) != EOF) {
                            if (isRegular(b)) {
                                buf.write(b);
                            } else {
                                break;
                            }
                        }
                        String str = buf.toString(charset.name());
                        Number num;
                        if (str.indexOf('.') != -1) {
                            num = Double.valueOf(str);
                        } else {
                            num = Long.valueOf(str);
                        }
                        if (b == RIGHT_BRACKET || b == RIGHT_BRACE) {
                            return new Finish(num);
                        }
                        return num;
                    }
                }
            }

        }
        return close;
    }

    private void writeMulti(PrintStream out, Object key, Collection value, boolean format, int depth) throws IOException {
        Iterator it = value.iterator();
        while (it.hasNext()) {
            if (format) {
                writeBreak(out, depth + writeSpaceCount);
            }
            write(out, key, format, depth + writeSpaceCount);
            out.write(COLON);
            if (format) {
                writeSpace(out, writeSpaceCount);
            }
            write(out, it.next(), format, depth + writeSpaceCount);
            if (it.hasNext()) {
                out.write(COMMA);
            }
        }

    }

    /**
     * Returns {@code true} if the specified {@code b} byte is regular
     * character, otherwise {@code false}.
     *
     * @param b the specified byte
     * @return {@code true} if the specified {@code b} byte is regular
     * character, otherwise {@code false}.
     */
    private boolean isRegular(int b) {
        return b > 32
                && b != LEFT_BRACKET
                && b != LEFT_BRACE
                && b != COMMA
                && b != RIGHT_BRACKET
                && b != RIGHT_BRACE;
    }

    /**
     * Returns the unescaped string.
     *
     * @param str the input source
     * @return the unescaped string.
     */
    private String unescape(String str) {
        str = str.replace("\\b", "\b");
        str = str.replace("\\f", "\f");
        str = str.replace("\\n", "\n");
        str = str.replace("\\r", "\r");
        str = str.replace("\\t", "\t");
        str = str.replace("\\\"", "\"");
        return str;
    }

    /**
     * Returns the escaped string.
     *
     * @param str the input source
     * @return the escaped string.
     */
    public static String escape(String str) {
        str = str.replace("\b", "\\b");
        str = str.replace("\f", "\\f");
        str = str.replace("\n", "\\n");
        str = str.replace("\r", "\\r");
        str = str.replace("\t", "\\t");
        str = str.replace("\"", "\\\"");
        return str;
    }

    /**
     * Writes spaces to a specified output {@code out}.
     *
     * @param out the specified output
     * @param spaceCount the spaces count
     * @throws IOException if IO Error Occurs.
     */
    private void writeSpace(OutputStream out, int spaceCount) throws IOException {
        byte[] b = new byte[spaceCount];
        for (int i = 0; i < b.length; i++) {
            b[i] = SP;
        }
        out.write(b);
    }

    /**
     * Writes break line and spaces to a specified output {@code out}.
     *
     * @param out the specified output
     * @param spaceCount the spaces count
     * @throws IOException if IO Error Occurs.
     */
    private void writeBreak(OutputStream out, int spaceCount) throws IOException {
        out.write(EOL);
        writeSpace(out, spaceCount);
    }

    /**
     * Creates a new instance of JSON.
     *
     * @return a new instance of JSON.
     */
    public static Json getInstance() {
        return new Json();
    }

    public Object read(byte[] b) throws IOException {
        return read(new ByteArrayInputStream(b));
    }

    /**
     * This class can contains json elements to one key. json objects (map) can
     * contains duplicate values to one key.
     */
    public static class DuplicatedKeyList extends AbstractList {

        private List list;

        public DuplicatedKeyList(List list) {
            if (list == null) {
                throw new NullPointerException("list is null");
            }
            this.list = list;
        }

        @Override
        public void add(int index, Object element) {
            list.add(index, element);
        }

        @Override
        public Object set(int index, Object element) {
            return list.set(index, element);
        }

        @Override
        public Object remove(int index) {
            return list.remove(index);
        }

        @Override
        public Object get(int index) {
            return list.get(index);
        }

        @Override
        public int size() {
            return list.size();
        }

        @Override
        public String toString() {
            Iterator it = iterator();
            if (!it.hasNext()) {
                return "[]";
            }
            StringBuilder sb = new StringBuilder();
            sb.append('[');
            for (;;) {
                sb.append('@').append('=');
                Object e = it.next();
                sb.append(e == this ? "(this Collection)" : e);
                if (!it.hasNext()) {
                    return sb.append(']').toString();
                }
                sb.append(',').append(' ');
            }
        }
    }

    /**
     * This class is a marker class which must use if the json collection (map
     * or list) will be finished.
     */
    class Finish {

        /**
         * the last object
         */
        private Object val;

        /**
         * Creates a new instance with the specified {@code val} value.
         *
         * @param val the specified value
         */
        public Finish(Object val) {
            this.val = val;
        }

    }

}