Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SQLLINE-83] JSON Outputformat support #84

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/manual.html
Original file line number Diff line number Diff line change
Expand Up @@ -1289,7 +1289,7 @@
!list List the current connections
!metadata Obtain metadata information
!outputformat Set the output format for displaying results
(table,vertical,csv,tsv,xmlattrs,xmlelements)
(table,vertical,csv,tsv,xmlattrs,xmlelements,json)
!properties Connect to the database specified in the properties file(s)
!primarykeys List all the primary keys for the specified table
!procedures List all the procedures
Expand Down
19 changes: 17 additions & 2 deletions src/docbkx/manual.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1127,7 +1127,7 @@ sqlline> !help
!metadata Obtain metadata information
!nickname Create a friendly name for the connection (updates command prompt)
!outputformat Set the output format for displaying results
(table, vertical, csv, tsv, xmlattrs, xmlelements)
(table, vertical, csv, tsv, xmlattrs, xmlelements, json)
!properties Connect to the database specified in the properties file(s)
!primarykeys List all the primary keys for the specified table
!procedures List all the procedures
Expand Down Expand Up @@ -1616,7 +1616,7 @@ NAME Microsoft
<title>Example of XML element output formatting</title>
<screen>
0: jdbc:oracle:thin:@localhost:1521:mydb> !outputformat xmlelements
0: jdbc:oracle:thin:@localhost:1521:mydb> SELECT * FROM COMPANY
0: jdbc:oracle:thin:@localhost:1521:mydb> SELECT * FROM COMPANY;
&lt;resultset>
&lt;result>
&lt;COMPANY_ID>1&lt;/COMPANY_ID>
Expand All @@ -1639,6 +1639,21 @@ NAME Microsoft
0: jdbc:oracle:thin:@localhost:1521:mydb>
</screen>
</refsect1>
<refsect1>
<title>Example of JSON output formatting</title>
<screen>
0: jdbc:calcite:model=target/test-classes/mod> !outputformat json
0: jdbc:calcite:model=target/test-classes/mod> SELECT * FROM COMPANY;
{"resultset":[
{"COMPANY_ID":1,"NAME":"Apple"},
{"COMPANY_ID":2,"NAME":"Sun"},
{"COMPANY_ID":3,"NAME":"IBM"},
{"COMPANY_ID":4,"NAME":"Microsoft"}
]}
4 rows selected (0.03 seconds)
0: jdbc:calcite:model=target/test-classes/mod>
</screen>
</refsect1>
</refentry>
</section>

Expand Down
147 changes: 147 additions & 0 deletions src/main/java/sqlline/JsonOutputFormat.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
// Licensed to Julian Hyde under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership.
//
// Julian Hyde licenses this file to you under the Modified BSD License
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at:
//
// http://opensource.org/licenses/BSD-3-Clause
*/
package sqlline;

import java.sql.SQLException;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;

/**
* Implementation of {@link OutputFormat} that formats rows as JSON.
*/
public class JsonOutputFormat extends AbstractOutputFormat {
private static final Map<Character, String> ESCAPING_MAP =
new HashMap<Character, String>();
static {
ESCAPING_MAP.put('\\', "\\\\");
ESCAPING_MAP.put('\"', "\\\"");
ESCAPING_MAP.put('\b', "\\b");
ESCAPING_MAP.put('\f', "\\f");
ESCAPING_MAP.put('\n', "\\n");
ESCAPING_MAP.put('\r', "\\r");
ESCAPING_MAP.put('\t', "\\t");
ESCAPING_MAP.put('/', "\\/");
ESCAPING_MAP.put('\u0000', "\\u0000");
ESCAPING_MAP.put('\u0001', "\\u0001");
ESCAPING_MAP.put('\u0002', "\\u0002");
ESCAPING_MAP.put('\u0003', "\\u0003");
ESCAPING_MAP.put('\u0004', "\\u0004");
ESCAPING_MAP.put('\u0005', "\\u0005");
ESCAPING_MAP.put('\u0006', "\\u0006");
ESCAPING_MAP.put('\u0007', "\\u0007");
// ESCAPING_MAP.put('\u0008', "\\u0008");
// covered by ESCAPING_MAP.put('\b', "\\b");
// ESCAPING_MAP.put('\u0009', "\\u0009");
// covered by ESCAPING_MAP.put('\t', "\\t");
// ESCAPING_MAP.put((char) 10, "\\u000A");
// covered by ESCAPING_MAP.put('\n', "\\n");
ESCAPING_MAP.put('\u000B', "\\u000B");
// ESCAPING_MAP.put('\u000C', "\\u000C");
// covered by ESCAPING_MAP.put('\f', "\\f");
// ESCAPING_MAP.put((char) 13, "\\u000D");
// covered by ESCAPING_MAP.put('\r', "\\r");
ESCAPING_MAP.put('\u000E', "\\u000E");
ESCAPING_MAP.put('\u000F', "\\u000F");
ESCAPING_MAP.put('\u0010', "\\u0010");
ESCAPING_MAP.put('\u0011', "\\u0011");
ESCAPING_MAP.put('\u0012', "\\u0012");
ESCAPING_MAP.put('\u0013', "\\u0013");
ESCAPING_MAP.put('\u0014', "\\u0014");
ESCAPING_MAP.put('\u0015', "\\u0015");
ESCAPING_MAP.put('\u0016', "\\u0016");
ESCAPING_MAP.put('\u0017', "\\u0017");
ESCAPING_MAP.put('\u0018', "\\u0018");
ESCAPING_MAP.put('\u0019', "\\u0019");
ESCAPING_MAP.put('\u001A', "\\u001A");
ESCAPING_MAP.put('\u001B', "\\u001B");
ESCAPING_MAP.put('\u001C', "\\u001C");
ESCAPING_MAP.put('\u001D', "\\u001D");
ESCAPING_MAP.put('\u001E', "\\u001E");
ESCAPING_MAP.put('\u001F', "\\u001F");
}
private int[] columnTypes;
public JsonOutputFormat(SqlLine sqlLine) {
super(sqlLine);
}

@Override
void printHeader(Rows.Row header) {
sqlLine.output("{\"resultset\":[");
}

@Override
void printFooter(Rows.Row header) {
sqlLine.output("]}");
}

@Override
void printRow(Rows rows, Rows.Row header, Rows.Row row) {
String[] head = header.values;
String[] vals = row.values;
StringBuilder sb = new StringBuilder("{");
for (int i = 0; (i < head.length) && (i < vals.length); i++) {
if (columnTypes == null) {
initColumnTypes(rows, header);
}
sb.append("\"").append(head[i]).append("\":");
setJsonValue(sb, vals[i], columnTypes[i]);
sb.append((i < head.length - 1) && (i < vals.length - 1) ? "," : "");
}
sb.append(rows.hasNext() ? "}," : "}");
sqlLine.output(sb.toString());
}

private void setJsonValue(StringBuilder sb, String value, int columnTypeId) {
if (value == null) {
sb.append(value);
return;
}
switch (columnTypeId) {
case Types.TINYINT:
case Types.SMALLINT:
case Types.INTEGER:
case Types.BIGINT:
case Types.REAL:
case Types.FLOAT:
case Types.DOUBLE:
case Types.DECIMAL:
case Types.NUMERIC:
case Types.BOOLEAN:
case Types.NULL:
sb.append(value);
return;
}
sb.append("\"");
for (int i = 0; i < value.length(); i++) {
if (ESCAPING_MAP.get(value.charAt(i)) != null) {
sb.append(ESCAPING_MAP.get(value.charAt(i)));
} else {
sb.append(value.charAt(i));
}
}
sb.append("\"");
}

private void initColumnTypes(Rows rows, Rows.Row header) {
columnTypes = new int[header.values.length];
for (int j = 0; j < header.values.length; j++) {
try {
columnTypes[j] = rows.rsMeta.getColumnType(j + 1);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}

// End JsonOutputFormat.java
3 changes: 2 additions & 1 deletion src/main/java/sqlline/SqlLine.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ public class SqlLine {
"csv", new SeparatedValuesOutputFormat(this, ','),
"tsv", new SeparatedValuesOutputFormat(this, '\t'),
"xmlattr", new XmlAttributeOutputFormat(this),
"xmlelements", new XmlElementOutputFormat(this));
"xmlelements", new XmlElementOutputFormat(this),
"json", new JsonOutputFormat(this));

final List<CommandHandler> commandHandlers;

Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/sqlline/SqlLine.properties
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ help-procedures: List all the procedures
help-tables: List all the tables in the database
help-columns: List all the columns for the specified table
help-properties: Connect to the database specified in the properties file(s)
help-outputformat: Set the output format for displaying results (table,vertical,csv,tsv,xmlattrs,xmlelements)
help-outputformat: Set the output format for displaying results (table,vertical,csv,tsv,xmlattrs,xmlelements,json)
help-nickname: Create a friendly name for the connection (updates command prompt)

jline-missing: SQLLine static class check reports the {0} class was not found. Please ensure JLine is on classpath.
Expand Down
17 changes: 15 additions & 2 deletions src/main/resources/sqlline/manual.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1010,7 +1010,7 @@ sqlline> !help
!list List the current connections
!metadata Obtain metadata information
!outputformat Set the output format for displaying results
(table,vertical,csv,tsv,xmlattrs,xmlelements)
(table,vertical,csv,tsv,xmlattrs,xmlelements,json)
!properties Connect to the database specified in the properties file(s)
!primarykeys List all the primary keys for the specified table
!procedures List all the procedures
Expand Down Expand Up @@ -1308,7 +1308,7 @@ Example of XML attribute output formatting
Example of XML element output formatting

0: jdbc:oracle:thin:@localhost:1521:mydb> !outputformat xmlelements
0: jdbc:oracle:thin:@localhost:1521:mydb> SELECT * FROM COMPANY
0: jdbc:oracle:thin:@localhost:1521:mydb> SELECT * FROM COMPANY;
<resultset>
<result>
<COMPANY_ID>1</COMPANY_ID>
Expand All @@ -1330,6 +1330,19 @@ Example of XML element output formatting
4 rows selected (0.02 seconds)
0: jdbc:oracle:thin:@localhost:1521:mydb>

Example of JSON output formatting

0: jdbc:calcite:model=target/test-classes/mod> !outputformat json
0: jdbc:calcite:model=target/test-classes/mod> SELECT * FROM COMPANY;
{"resultset":[
{"COMPANY_ID":1,"NAME":"Apple"},
{"COMPANY_ID":2,"NAME":"Sun"},
{"COMPANY_ID":3,"NAME":"IBM"},
{"COMPANY_ID":4,"NAME":"Microsoft"}
]}
4 rows selected (0.03 seconds)
0: jdbc:calcite:model=target/test-classes/mod>

primarykeys

Name
Expand Down
13 changes: 13 additions & 0 deletions src/test/java/sqlline/SqlLineArgsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,19 @@ public void testTablesCsv() throws Throwable {
containsString("'PUBLIC','SCOTT','SALGRADE','TABLE','',")));
}

@Test
public void testTablesJson() throws Throwable {
final String script = "!set outputformat json\n"
+ "!tables\n";
checkScriptFile(script, true, equalTo(SqlLine.Status.OK),
allOf(
containsString("{\"resultset\":["),
containsString("{\"TABLE_CAT\":\"PUBLIC\","
+ "\"TABLE_SCHEM\":\"SYSTEM_LOBS\",\"TABLE_NAME\":\"BLOCKS\","
+ "\"TABLE_TYPE\":\"SYSTEM TABLE\",\"REMARKS\":null,"
+ "\"TYPE_CAT\":null,")));
}

@Test
public void testTimeFormat() throws Throwable {
// Use System.err as it is used in sqlline.SqlLineOpts#set
Expand Down