简体   繁体   English

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

[英]Convert JSON file into RDF format using Java

Here I want to convert the JSON file into RDF.这里我想把 JSON 文件转换成 RDF。 Here is the JSON这是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"
            }
        }
    }
}
}

I could not find a proper way to convert it into RDF.我找不到将其转换为 RDF 的正确方法。

There is no standard way to interpret JSON as RDF.没有将 JSON 解释为 RDF 的标准方法。 There are several ways you can generate RDF from a JSON file though (in Java or otherwise).有几种方法可以从 JSON 文件生成 RDF(Java 或其他)。 You could simply use a JSON parser implemented in Java, then extract the relevant parts and build an RDF graph using a Java library for RDF such as Apache Jena or RDF4J (formerly known as Sesame ).您可以简单地使用在 Java 中实现的 JSON 解析器,然后提取相关部分并使用 RDF 的 Java 库(例如Apache JenaRDF4J (以前称为Sesame ))构建 RDF 图。 However, there are other ways that could make the task much easier:但是,还有其他方法可以使任务变得更容易:

  • Transform the JSON file into a JSON-LD file by adding a @context to it.通过向其中添加@context将 JSON 文件转换为JSON-LD文件。 This works well for simple cases but is not sufficient to cover many relevant cases.这适用于简单的案例,但不足以涵盖许多相关案例。
  • Use RML , a language for expressing mappings from various data formats (including JSON) to RDF.使用RML ,一种表达从各种数据格式(包括 JSON)到 RDF 的映射的语言。 It has a reference implementation in Java.它在 Java 中有一个参考实现。 RML is an extension of R2RML, so it can also map relational data to RDF, and if you are familiar with R2RML , it is relatively easy to understand how RML works. RML是R2RML的扩展,因此也可以将关系数据映射到RDF,如果熟悉R2RML ,就比较容易理解RML的工作原理。 There is also a graphical editor , but it seems it is not available for download.还有一个图形编辑器,但似乎无法下载。
  • Use SPARQL-Generate , a language for expressing mappings from non-RDF data sources (including JSON) to RDF.使用SPARQL-Generate ,一种表达从非 RDF 数据源(包括 JSON)到 RDF 的映射的语言。 It has a reference implementation based on Jena.它有一个基于 Jena 的参考实现。 It extends SPARQL, so if you are familiar with SPARQL, it should be quite easy to use it.它扩展了 SPARQL,所以如果您熟悉 SPARQL,它应该很容易使用。 It can be tested online .它可以 在线测试

Disclaimer: I contributed to SPARQL-Generate.免责声明:我为 SPARQL-Generate 做出了贡献。

If your aim is to simply get valid RDF without making any decisions about structure, you could trivially add a @context object and turn the existing JSON into JSON-LD, eg如果您的目标是简单地获得有效的 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"
            }
        }
    }
  }
}

This can then be interpreted as RDF using an appropriate RDF/JSON-LD library, the RDF structure would be:然后可以使用适当的 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"
  ] .

This is perhaps strange RDF, but it can be loaded and manipulated using RDF tools.这可能是奇怪的 RDF,但可以使用 RDF 工具加载和操作它。

You can play with the example in the json-ld playground你可以在json-ld playground玩这个例子

You can convert the JSON file into RDF using RML .您可以使用RML将 JSON 文件转换为 RDF。 To achieve that, you need to create some mapping rules first.为此,您需要先创建一些映射规则。 These mapping rules are used by an RML processor to transform your input data into RDF. RML 处理器使用这些映射规则将您的输入数据转换为 RDF。 An example of an RML processor is the RML mapper . RML 处理器的一个例子是RML 映射器

  1. Create an RML LogicalSource .创建一个 RML LogicalSource This describes how and where the RML processor should access the input data:这描述了 RML 处理器应该如何以及在哪里访问输入数据:
<#LogicalSource>
    a rml:logicalSource;
    rml:source "data.json";
    rml:referenceFormulation ql:JSONPath;
    rml:iterator "$.glossary".

This RDF snippet tells an RML processor that he has to retrieve data.json and iterate over the data with a JSONPath expression ( ql:JSONPath ) which is specified by rml:iterator .这个 RDF 片段告诉 RML 处理器他必须检索data.json并使用rml:iterator指定的 JSONPath 表达式 ( ql:JSONPath ) rml:iterator

  1. Create a TriplesMap .创建一个TriplesMap This provides an RML processor information about how the input data should be transformed into RDF.这提供了有关如何将输入数据转换为 RDF 的 RML 处理器信息。 An example is given for the provided data:为提供的数据给出了一个示例:
<#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";
        ];
    ].

The TriplesMap uses the created LogicalSource to access the data. TriplesMap 使用创建的 LogicalSource 来访问数据。 The TriplesMap will make sure that an RML processor creates triples with http://example.org/glossary/{title} as subject, ex:title as predicate and the value of the JSON property title as object TriplesMap 将确保 RML 处理器创建以http://example.org/glossary/{title}作为主语、 ex:title作为谓词和 JSON 属性title作为对象的三元组

  1. TriplesMaps can also be linked with each other. TriplesMap 也可以相互链接。 If you have an entry of a list ( GlossEntry ) to a higher element glossary for example, you can write the following mapping rules:例如,如果您有一个列表条目 ( 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}";
    ];

Both TriplesMaps will be joined with each other taking into account the provided rr:joinCondition .考虑到提供的rr:joinCondition两个 TriplesMap 将相互连接。 If no join condition is provided, every subject of the child will be joined with every subject of the parent.如果未提供加入条件,则子项的每个主题都将与父项的每个主题相连。

Mapping rules映射规则

I left out some JSON properties in this full example to keep it concise.为了保持简洁,我在这个完整示例中省略了一些 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.[*]";
        ];
    ].

Output输出

<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>.

Note : I contribute to RML and its technologies.注意:我为 RML 及其技术做出了贡献。

If I understand your question correctly, I recommend using Apache Jena , Apache's open source semantic web library.如果我正确理解您的问题,我建议使用Apache Jena ,Apache 的开源语义网络库。

Here is an RDF creation tutorial .这是一个RDF 创建教程

I've also found JSON2RDF , a project that claims to utilize Jena in creating a declerative language for converting JSONs into RDFs, but I have not tested it myself and the docs are vague.我还发现了JSON2RDF ,这是一个声称利用 Jena 创建将 JSON 转换为 RDF 的声明式语言的项目,但我自己没有测试过,而且文档含糊不清。 Please share your experience if you do end up looking into it.如果您最终研究它,请分享您的经验。

We have released open-source JSON2RDF converter (not to be confused with the JSON2RDF mentioned by vaiden) that allows you to do exactly that: https://github.com/AtomGraph/JSON2RDF我们已经发布了开源 JSON2RDF 转换器(不要与 vaiden 提到的 JSON2RDF 混淆),它允许您做到这一点: https : //github.com/AtomGraph/JSON2RDF

JSON2RDF + SPARQL can be used as a more flexible alternative to JSON-LD + @context . JSON2RDF + SPARQL 可以用作 JSON-LD + @context的更灵活的替代方案。

We're providing a JSON to JSONLD converting service: https://json2ld.mapper.tokyo/我们提供 JSON 到 JSONLD 的转换服务: https ://json2ld.mapper.tokyo/

First, you paste or upload your JSON data, then it instantly generates their JSONLD version.首先,您粘贴或上传您的 JSON 数据,然后它会立即生成它们的 JSONLD 版本。

It automatically maps some existing vocabularies such as schema.org if there's a matching term in the JSON data such as "name", which is mapped to https://schema.org/name .如果 JSON 数据中存在匹配的术语(例如映射到https://schema.org/name 的“name”),它会自动映射一些现有词汇表,例如 schema.org。

You can edit which vocabularies or terms are mapped and publish your context from your GitHub repo.您可以编辑映射的词汇表或术语,并从您的 GitHub 存储库发布您的上下文。

I spent some time recently to paste some older java code together to create a command line tool named 'oi'.我最近花了一些时间将一些旧的java代码粘贴在一起,以创建一个名为“oi”的命令行工具。

https://github.com/jschnasse/oi https://github.com/jschnasse/oi

Call称呼

oi stack44753298.json -t turtle

Prints印刷

_: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" .

The tool uses a very rough approach to transform rdf to json and vice versa.该工具使用非常粗略的方法将 rdf 转换为 json,反之亦然。 For the json to rdf case it creates an adhoc context just by putting all json keys into an info:namespace .对于 json 到 rdf 的情况,它只是通过将所有 json 键放入info:namespace来创建一个临时上下文。 After that it uses the context to read in the json as jsonld.之后,它使用上下文将 json 读取为 jsonld。 Exactly like already described in this thread by brinxmat .brinxmat在此线程中已经描述的完全一样。

It follows roughly the ideas I wrote up in recent stackoverflow threads:它大致遵循我在最近的 stackoverflow 线程中写的想法:

Read JSON with this JSON parser to Java objects and create rdf with Java objects which is previously read.使用此 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;
        }

    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM