MySQL ResultSet into Gson array - json

I want to put a MySQL result set into a JsonArray using Gsons library. How can I best achieve this. I've read this:
resultset to json using gson
But it uses for some reason, the simple-Json library in addition. i dont want that if possible. Is there any way to achieve this easily with the gson library?
Thank you very much!
PlayerList.java:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package de.freakyonline.ucone;
import de.freakyonline.ucone.Player;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.TextArea;
/**
*
* #author uwe
*/
public class PlayerList {
ObservableList<Player> playerList;
ObjectInputStream in;
ObjectOutputStream out;
Socket sock;
private Object obj = null;
private Object obj2 = null;
TextArea consoleOneTextArea;
public PlayerList(ObjectInputStream in, ObjectOutputStream out, Socket sock, TextArea consoleOneTextArea) {
this.in = in;
this.out = out;
this.sock = sock;
this.consoleOneTextArea = consoleOneTextArea;
getPlayersFromServer();
}
private void getPlayersFromServer() {
/* try {
out.writeObject("getplayers");
obj=in.readObject();
if(obj == null) {
System.out.println("ERROR! void getPlayersFromServer in PlayerList.java");
Platform.exit();
}
String command = obj.toString().toLowerCase();
String currentFromServer;
if(command.equalsIgnoreCase("getplayers")) {
while((obj2=in.readObject()) != null) {
currentFromServer = obj2.toString().toLowerCase();
for(String cell : currentFromServer.split(" ")) {
System.out.println(cell.toString());
}
if (currentFromServer.equalsIgnoreCase("done")) {
consoleOneTextArea.appendText("This is finished. Have fun!\n");
break;
}
consoleOneTextArea.appendText(currentFromServer + "\n");
}
} { System.out.println("ERROR"); }
} catch (Exception ex) { ex.printStackTrace(); }
*/
this.playerList = FXCollections.observableArrayList(
new Player("freakyy85","Owner","1810",31,"m", "missing..."),
new Player("Ky3ak","Owner","1920",34,"m", "missing...")
);
}
}
(ive commented out some parts, as they are not relevant anymore)
Player.java:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package de.freakyonline.ucone;
import com.google.gson.stream.JsonReader;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import javafx.scene.control.TextArea;
/**
*
* #author uwe
*/
public class Remote implements Runnable {
private Object obj = null;
private Object obj2 = null;
private ObjectInputStream in;
private ObjectOutputStream out;
private Socket sock;
private TextArea consoleOneTextArea;
public Remote (ObjectInputStream in, ObjectOutputStream out, Socket sock) {
this.in = in;
this.out = out;
this.sock = sock;
}
public ObjectInputStream getIn() {
return in;
}
public ObjectOutputStream getOut() {
return out;
}
public Socket getSock() {
return sock;
}
public void setConsoleOneTextArea(TextArea consoleOneTextArea) {
this.consoleOneTextArea = consoleOneTextArea;
}
public void run() {
try {
while((obj=in.readObject()) != null && sock.isConnected()) {
String command = obj.toString().toLowerCase();
String currentFromServer;
switch(command) {
case "getplayers":
/* while((obj2=in.readObject()) != null) {
currentFromServer = obj2.toString().toLowerCase();
if (currentFromServer.equalsIgnoreCase("done")) {
consoleOneTextArea.appendText("This is finished. Have fun!\n");
break;
}
consoleOneTextArea.appendText(currentFromServer + "\n");
*/ }
JsonReader jsonReader = new JsonReader(new InputStreamReader(in, "UTF-8"));
jsonReader.close();
break;
}
} catch (Exception ex) { ex.printStackTrace(); }
}
}

Is there any way to achieve this easily with the gson library?
Not really. Gson and JDBC are too/two unrelated things so you have to implement a custom remapping function to "decode" JDBC result set rows/fields and "encode" them back to JSON array/object respectively. Accumulating a JsonArray instance may be expensive from the memory consumption point of view, or even crash the application with OutOfMemoryError for huge result sets. Nonetheless they are good if the result sets are known to be small or LIMITed.
Accumulating JsonArray
static JsonArray resultSetToJsonArray(final ResultSet resultSet)
throws SQLException {
final ResultSetMetaData metaData = resultSet.getMetaData();
// JsonArray is a Gson built-in class to hold JSON arrays
final JsonArray jsonArray = new JsonArray();
while ( resultSet.next() ) {
jsonArray.add(resultSetRowToJsonObject(resultSet, metaData));
}
return jsonArray;
}
private static JsonElement resultSetRowToJsonObject(final ResultSet resultSet, final ResultSetMetaData metaData)
throws SQLException {
final int columnCount = metaData.getColumnCount();
// Every result set row is a JsonObject equivalent
final JsonObject jsonObject = new JsonObject();
// JDBC uses 1-based loops
for ( int i = 1; i <= columnCount; i++ ) {
jsonObject.add(metaData.getColumnName(i), fieldToJsonElement(resultSet, metaData, i));
}
return jsonObject;
}
private static JsonElement fieldToJsonElement(final ResultSet resultSet, final ResultSetMetaData metaData, final int column)
throws SQLException {
final int columnType = metaData.getColumnType(column);
final Optional<JsonElement> jsonElement;
// Process each SQL type mapping a value to a JSON tree equivalent
switch ( columnType ) {
case Types.BIT:
case Types.TINYINT:
case Types.SMALLINT:
throw new UnsupportedOperationException("TODO: " + JDBCType.valueOf(columnType));
case Types.INTEGER:
// resultSet.getInt() returns 0 in case of null, so it must be extracted with getObject and cast, then converted to a JsonPrimitive
jsonElement = Optional.ofNullable((Integer) resultSet.getObject(column)).map(JsonPrimitive::new);
break;
case Types.BIGINT:
case Types.FLOAT:
case Types.REAL:
case Types.DOUBLE:
case Types.NUMERIC:
case Types.DECIMAL:
case Types.CHAR:
throw new UnsupportedOperationException("TODO: " + JDBCType.valueOf(columnType));
case Types.VARCHAR:
jsonElement = Optional.ofNullable(resultSet.getString(column)).map(JsonPrimitive::new);
break;
case Types.LONGVARCHAR:
case Types.DATE:
case Types.TIME:
case Types.TIMESTAMP:
case Types.BINARY:
case Types.VARBINARY:
case Types.LONGVARBINARY:
case Types.NULL:
case Types.OTHER:
case Types.JAVA_OBJECT:
case Types.DISTINCT:
case Types.STRUCT:
case Types.ARRAY:
case Types.BLOB:
case Types.CLOB:
case Types.REF:
case Types.DATALINK:
case Types.BOOLEAN:
case Types.ROWID:
case Types.NCHAR:
case Types.NVARCHAR:
case Types.LONGNVARCHAR:
case Types.NCLOB:
case Types.SQLXML:
case Types.REF_CURSOR:
case Types.TIME_WITH_TIMEZONE:
case Types.TIMESTAMP_WITH_TIMEZONE:
throw new UnsupportedOperationException("TODO: " + JDBCType.valueOf(columnType));
default:
throw new UnsupportedOperationException("Unknown type: " + columnType);
}
// If the optional value is missing, assume it's a null
return jsonElement.orElse(JsonNull.INSTANCE);
}
final JsonArray jsonArray = resultSetToJsonArray(resultSet);
System.out.println(jsonArray);
Don't forget to close the resultSet, of course.
JSON streaming
If the JsonArray is supposed to be written elsewhere, JsonWriter can be a better solution being able to process huge result sets reading row by row and writing JSON element by JSON element.
#SuppressWarnings("resource")
static void resultSetToJsonArrayStream(final ResultSet resultSet, final JsonWriter jsonWriter)
throws SQLException, IOException {
// Write the [ token
jsonWriter.beginArray();
final ResultSetMetaData metaData = resultSet.getMetaData();
while ( resultSet.next() ) {
// Write row by row
writeRow(resultSet, jsonWriter, metaData);
}
// Finish the array with the ] token
jsonWriter.endArray();
}
#SuppressWarnings("resource")
private static void writeRow(final ResultSet resultSet, final JsonWriter jsonWriter, final ResultSetMetaData metaData)
throws SQLException, IOException {
final int columnCount = metaData.getColumnCount();
// Similarly to the outer array: the { token starts a new object representing a row
jsonWriter.beginObject();
for ( int i = 1; i <= columnCount; i++ ) {
// Write the column name and try to resolve a JSON literal to be written
jsonWriter.name(metaData.getColumnName(i));
writeField(resultSet, jsonWriter, metaData, i);
}
// Terminate the object with }
jsonWriter.endObject();
}
#SuppressWarnings("resource")
private static void writeField(final ResultSet resultSet, final JsonWriter jsonWriter, final ResultSetMetaData metaData, final int column)
throws SQLException, IOException {
final int columnType = metaData.getColumnType(column);
switch ( columnType ) {
case Types.BIT:
case Types.TINYINT:
case Types.SMALLINT:
throw new UnsupportedOperationException("TODO: " + JDBCType.valueOf(columnType));
case Types.INTEGER:
jsonWriter.value((Integer) resultSet.getObject(column));
break;
case Types.BIGINT:
case Types.FLOAT:
case Types.REAL:
case Types.DOUBLE:
case Types.NUMERIC:
case Types.DECIMAL:
case Types.CHAR:
throw new UnsupportedOperationException("TODO: " + JDBCType.valueOf(columnType));
case Types.VARCHAR:
jsonWriter.value((String) resultSet.getObject(column));
break;
case Types.LONGVARCHAR:
case Types.DATE:
case Types.TIME:
case Types.TIMESTAMP:
case Types.BINARY:
case Types.VARBINARY:
case Types.LONGVARBINARY:
case Types.NULL:
case Types.OTHER:
case Types.JAVA_OBJECT:
case Types.DISTINCT:
case Types.STRUCT:
case Types.ARRAY:
case Types.BLOB:
case Types.CLOB:
case Types.REF:
case Types.DATALINK:
case Types.BOOLEAN:
case Types.ROWID:
case Types.NCHAR:
case Types.NVARCHAR:
case Types.LONGNVARCHAR:
case Types.NCLOB:
case Types.SQLXML:
case Types.REF_CURSOR:
case Types.TIME_WITH_TIMEZONE:
case Types.TIMESTAMP_WITH_TIMEZONE:
throw new UnsupportedOperationException("TODO: " + JDBCType.valueOf(columnType));
default:
throw new UnsupportedOperationException("Unknown type: " + columnType);
}
}
Example of writing to System.out, but, of course, it can be written anywhere just supplying an appropriate OutputStream instance:
final JsonWriter jsonWriter = new JsonWriter(new OutputStreamWriter(System.out))
resultSetToJsonArrayStream(resultSet, jsonWriter);
Similarly to ResultSet, JsonWriter must be closed as well.
I've written the above code for SQLite, but it should work for MySQL too. For example, the test database created and populated with the following SQL statements:
CREATE TABLE `table` (i NUMBER NOT NULL, s TEXT NOT NULL);
INSERT INTO `table` (i, s) VALUES (1, 'foo');
INSERT INTO `table` (i, s) VALUES (2, 'bar');
INSERT INTO `table` (i, s) VALUES (3, 'baz');
will result in
[{"i":1,"s":"foo"},{"i":2,"s":"bar"},{"i":3,"s":"baz"}]
for both object model and streaming approaches.

Related

Apache Flink : Window Function on AllWindowed Stream - Combining Kafka Topics

I am trying to combine two kafka topics using the a single kafka consumer on a list of topics, further convert the json string in the stream to POJO. Then, join them via keyBy ( On event time field ) and to merge them as a single fat json, I was planning to use a window stream and apply a window function on the window stream. The assumption is that Topic-A & Topic-B can be joined on Event Time and only one pair ( Topic A ( JSON ) , Topic B (JSON ) will be present with the same eventTime. Hence was planning to use a coutWindow(2) post keyBy on eventTime.
I have couple of questions for the same;
Is the approach fine for merging topics and creating a single JSON?
The window function on All Window stream doesnt seem to work fine; Any pointers will be greatly appreciated.
Code Snippet :
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
logger.info("Flink Stream Window Charger has started");
Properties properties = new Properties();
properties.setProperty("bootstrap.servers", "127.0.0.1:1030");
properties.setProperty("zookeeper.connect", "127.0.0.1:2181/service-kafka");
properties.setProperty("group.id", "group-0011");
properties.setProperty("auto.offset.reset", "smallest");
List < String > names = new ArrayList < > ();
names.add("Topic-A");
names.add("Topic-B");
DataStream < String > stream = env.addSource(new FlinkKafkaConsumer08 < > (names, new SimpleStringSchema(), properties));
DataStream < TopicPojo > pojo = stream.map(new Deserializer()).keyBy((eventTime) -> TopicPojo.getEventTime());
List < String > where = new ArrayList < String > ();
AllWindowedStream < String, GlobalWindow > data_window = pojo.flatMap(new Tokenizer()).countWindowAll(2);
DataStream < String > data_charging = data_window.apply(new MyWindowFunction());
data_charging.addSink(new SinkFunction < String > () {
public void invoke(String value) throws Exception {
// Yet to be implemented - Merge two POJO into one
}
});
try
{
env.execute();
} catch (Exception e)
{
return;
}
}
}
class Tokenizer implements FlatMapFunction < TopicPojo, String > {
private static final long serialVersionUID = 1 L;
#Override
public void flatMap(TopicPojo value, Collector < String > out) throws Exception {
ObjectMapper mapper = new ObjectMapper();
out.collect(mapper.writeValueAsString(value));
}
}
class MyWindowFunction implements WindowFunction < TopicPojo, String, String, GlobalWindow > {
#Override
public void apply(String key, GlobalWindow window, Iterable < TopicPojo > arg2, Collector < String > out)
throws Exception {
int count = 0;
for (TopicPojo in : arg2) {
count++;
}
// Test Result - TO be modified
out.collect("Window: " + window + "count: " + count);
}
}
class Deserializer implements MapFunction < String, TopicPojo > {
private static final long serialVersionUID = 1 L;
#Override
public TopicPojo map(String value) throws IOException {
// TODO Auto-generated method stub
ObjectMapper mapper = new ObjectMapper();
TopicPojo obj = null;
try {
System.out.println(value);
obj = mapper.readValue(value, TopicPojo.class);
} catch (JsonParseException e) {
// TODO Auto-generated catch block
throw new IOException("Failed to deserialize JSON object.");
} catch (JsonMappingException e) {
// TODO Auto-generated catch block
throw new IOException("Failed to deserialize JSON object.");
} catch (IOException e) {
// TODO Auto-generated catch block
throw new IOException("Failed to deserialize JSON object.");
}
return obj;
}
}
I am getting -
The method apply(AllWindowFunction) in the type AllWindowedStream is not applicable for the arguments (MyWindowFunction) error.
An AllWindowedStream is a non-keyed stream, and so the apply method for AllWindowedStreams doesn't have a key parameter. Since you are windowing a keyed stream, your data_window should be a KeyedStream.

Handle JSON which sends array of items but sometimes empty string in case of 0 elements

I have a JSON which sends array of element in normal cases but sends empty string "" tag without array [] brackets in case of 0 elements.
How to handle this with Gson? I want to ignore the error and not cause JSONParsingException.
eg.
"types": [
"Environment",
"Management",
"Computers"
],
sometimes it returns:
"types" : ""
Getting the following exception: Expected BEGIN ARRAY but was string
Since you don't have control over the input JSON string, you can test the content and decide what to do with it.
Here is an example of a working Java class:
import com.google.gson.Gson;
import java.util.ArrayList;
public class Test {
class Types {
Object types;
}
public void test(String input) {
Gson gson = new Gson();
Types types = gson.fromJson(input,Types.class);
if(types.types instanceof ArrayList) {
System.out.println("types is an ArrayList");
} else if (types.types instanceof String) {
System.out.println("types is an empty String");
}
}
public static void main(String[] args) {
String input = "{\"types\": [\n" +
" \"Environment\",\n" +
" \"Management\",\n" +
" \"Computers\"\n" +
" ]}";
String input2 = "{\"types\" : \"\"}";
Test testing = new Test();
testing.test(input2); //change input2 to input
}
}
If a bad JSON schema is not under your control, you can implement a specific type adapter that would try to determine whether the given JSON document is fine for you and, if possible, make some transformations. I would recomment to use #JsonAdapter in order to specify improperly designed types (at least I hope the entire API is not improperly designed).
For example,
final class Wrapper {
#JsonAdapter(LenientListTypeAdapterFactory.class)
final List<String> types = null;
}
where LenientListTypeAdapterFactory can be implemented as follows:
final class LenientListTypeAdapterFactory
implements TypeAdapterFactory {
// Gson can instantiate it itself, let it just do it
private LenientListTypeAdapterFactory() {
}
#Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
// Obtaining the original list type adapter
#SuppressWarnings("unchecked")
final TypeAdapter<List<?>> realListTypeAdapter = (TypeAdapter<List<?>>) gson.getAdapter(typeToken);
// And wrap it up in the lenient JSON type adapter
#SuppressWarnings("unchecked")
final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) new LenientListTypeAdapter(realListTypeAdapter);
return castTypeAdapter;
}
private static final class LenientListTypeAdapter
extends TypeAdapter<List<?>> {
private final TypeAdapter<List<?>> realListTypeAdapter;
private LenientListTypeAdapter(final TypeAdapter<List<?>> realListTypeAdapter) {
this.realListTypeAdapter = realListTypeAdapter;
}
#Override
public void write(final JsonWriter out, final List<?> value)
throws IOException {
realListTypeAdapter.write(out, value);
}
#Override
public List<?> read(final JsonReader in)
throws IOException {
// Check the next (effectively current) JSON token
switch ( in.peek() ) {
// If it's either `[...` or `null` -- we're supposing it's a "normal" list
case BEGIN_ARRAY:
case NULL:
return realListTypeAdapter.read(in);
// Is it a string?
case STRING:
// Skip the value entirely
in.skipValue();
// And return a new array list.
// Note that you might return emptyList() but Gson uses mutable lists so we do either
return new ArrayList<>();
// Not anything known else?
case END_ARRAY:
case BEGIN_OBJECT:
case END_OBJECT:
case NAME:
case NUMBER:
case BOOLEAN:
case END_DOCUMENT:
// Something definitely unexpected
throw new MalformedJsonException("Cannot parse " + in);
default:
// This would never happen unless Gson adds a new type token
throw new AssertionError();
}
}
}
}
Here is it how it can be tested:
for ( final String name : ImmutableList.of("3-elements.json", "0-elements.json") ) {
try ( final Reader reader = getPackageResourceReader(Q43562427.class, name) ) {
final Wrapper wrapper = gson.fromJson(reader, Wrapper.class);
System.out.println(wrapper.types);
}
}
Output:
[Environment, Management, Computers]
[]
If the entire API uses "" for empty arrays, then you can drop the #JsonAdapter annotation and register the LenientListTypeAdapterFactory via GsonBuilder, but add the following lines to the create method in order not to break other type adapters:
if ( !List.class.isAssignableFrom(typeToken.getRawType()) ) {
// This tells Gson to try to pick up the next best-match type adapter
return null;
}
...
There are a lot of weirdly designed JSON response choices, but this one hits the top #1 issue where nulls or empties are represented with "". Good luck!
Thanks for all your answers.
The recommed way as mentioned in above answers would be to use TypeAdapters and ExclusionStrategy for GSON.
Here is a good example Custom GSON desrialization

Send Json of ResultSet over Restful services without using POJO class or by using just 1 POJO class

I'm trying to implement restful services which will send Json from ResultSet of PostgreSQL, I was able to send by mapping resultset to a POJO class using Jersey and I dont want to use any other library like Gson, but I would like to send resultset directly as Json without the intermediate POJOs as I'm not in need of POJOs for any other purpose, I'm not very much familiar with servlets or restfull or JAXB, etc
I was able to create a custom JsonArray(of Javax.json) from ResultSet without POJO class and convert toString and send String on service, as of now for numbers, strings, dates, timestamps(can even try and do for arrays using recursion, didn't write Custom MessageBodyWriter but can write simple ones, may be even pretend even to write for this) but didn't understand perfectly whether what i'm doing is right way of doing or not and how to handle all data types of PostgreSQL to Json and send over Rest.
Please give me suggestions of what are the ways to implement this, and
Please comment little advance things and how they are inter related more clearly on Jersey, JAXB, JAX-RS, for e.g., like how a POJO is getting converted to Json internally, where is that MessageBodyWriter which is converting.
Kindly give me suggestion related to jersey and not other libraries.
Thank you.
Please find the below code I have written and suggest me even improvements.
Resource Class :
#Path("/message")
public class MessageResource {
MessageService msgService = new MessageService();
Test t = new Test();
#GET
#Produces(MediaType.TEXT_PLAIN)
public String getMessages() throws SQLException {
return t.getAllMessages();
}
}
Service Class :
public class Test {
private static ResultSetMetaData rsmd;
private static int numOfColumns;
PostgreHelper help = new PostgreHelper();
public String getAllMessages() throws SQLException {
RetrieveFromPostGre getData = new RetrieveFromPostGre();
Map<String, ?> config = null;
JsonBuilderFactory factory = Json.createBuilderFactory(config);
JsonObjectBuilder rowJsonObject = factory.createObjectBuilder();
JsonArrayBuilder tableJsonArray = factory.createArrayBuilder();
getData.selectRows("db1", "schema1", "table1");
ResultSet rs = getData.selectRows("db1", "schema1", "table1");
rsmd=rs.getMetaData();
numOfColumns = rsmd.getColumnCount();
try {
while(rs.next()) {
createJsonObjectOfSingleRow(rs, rowJsonObject);
tableJsonArray.add(rowJsonObject);
}
} catch (Exception e) {
System.out.println("error");
}
return tableJsonArray.build().toString();
}
public void createJsonObjectOfSingleRow(ResultSet rs, JsonObjectBuilder rowJsonObject) throws SQLException {
String numberType[] = {"BOOLEAN", "INTEGER", "FLOAT", "LONG", "DOUBLE"};
String stringType[] = {"STRING", "DATE"};
for (int i=1; i<=numOfColumns; i++) {
String column_name = rsmd.getColumnName(i);
System.out.println(help.getDataType(rsmd.getColumnType(i)));
if(Arrays.asList(numberType).contains(help.getDataType(rsmd.getColumnType(i)))) {
System.out.println("in num");
rowJsonObject.add(column_name, rs.getInt(column_name));
}
else if(Arrays.asList(stringType).contains(help.getDataType(rsmd.getColumnType(i)))) {
System.out.println("in stri");
rowJsonObject.add(column_name, rs.getString(column_name));
}
else if(help.getDataType(rsmd.getColumnType(i)).equalsIgnoreCase("ARRAY")) {
/* This is not working, and here I was thinking of doing recursion*/
//System.out.println("in arr");
//JsonArray jArray = (JsonArray) rs.getArray(column_name);
//rowJsonObject.add(column_name, jArray);
rowJsonObject.add("array", "flag");//just for the sake of running code successfully wrote this statement
}
}
}
}
PostgreServices :
public class RetrieveFromPostGre {
PostgreConnection postgreConnection = new PostgreConnection();
PostgreHelper getColumnsMeta = new PostgreHelper();
private String databaseName, schemaName, tableName;
private Map<String, String> columns = new HashMap<String, String>();
public String getDatabaseName() {
return databaseName;
}
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
public String getSchemaName() {
return schemaName;
}
public void setSchemaName(String schemaName) {
this.schemaName = schemaName;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public Map<String, String> getColumns() {
return columns;
}
public void setColumns(Map<String, String> columns) {
this.columns = columns;
}
public ResultSet selectRows(String databaseName, String schemaName, String tableName) throws SQLException {
this.setDatabaseName(databaseName);
this.setSchemaName(schemaName);
this.setTableName(tableName);
Connection dbConnection = postgreConnection.getdbconnection(this.getDatabaseName());
ResultSet rs = this.retrieveRows(dbConnection);
return rs;
}
public ResultSet retrieveRows(Connection dbConnection) throws SQLException {
Statement stmt = null;
ResultSet rs = null;
String sql;
sql = "select * from " + this.schemaName + "." + this.tableName;
try {
stmt = dbConnection.createStatement();
rs = stmt.executeQuery(sql);
return rs;
//stmt.close();
} catch (Exception e) {
System.err.println( e.getClass().getName()+": "+ e.getMessage() );
e.printStackTrace();
System.exit(0);
}
return rs;
}
}
DatabaseHelper :
public class PostgreHelper {
public String getDataType(int i) {
String type="";
switch(i) {
case java.sql.Types.INTEGER:
type="INTEGER"; break;
case java.sql.Types.BIGINT:
type="LONG"; break;
case java.sql.Types.BOOLEAN:
type="BOOLEAN"; break;
case java.sql.Types.BIT:
type="BOOLEAN"; break;
case java.sql.Types.FLOAT:
type="FLOAT"; break;
case java.sql.Types.REAL:
type="DOUBLE"; break;
case java.sql.Types.DOUBLE:
type="DOUBLE"; break;
case java.sql.Types.NUMERIC:
type="INTEGER"; break;
case java.sql.Types.DECIMAL:
type="DOUBLE"; break;
case java.sql.Types.CHAR:
type="STRING"; break;
case java.sql.Types.NCHAR:
type="STRING"; break;
case java.sql.Types.VARCHAR:
type="STRING"; break;
case java.sql.Types.NVARCHAR:
type="STRING"; break;
case java.sql.Types.LONGVARCHAR:
type="STRING"; break;
case java.sql.Types.DATE:
type="DATE"; break;
case java.sql.Types.TIME:
type="DATE"; break;
case java.sql.Types.TIMESTAMP:
type="DATE"; break;
case java.sql.Types.BINARY:
type="BINARY"; break;
case java.sql.Types.VARBINARY:
type="BINARY"; break;
case java.sql.Types.LONGVARBINARY:
type="BINARY"; break;
case java.sql.Types.NULL:
type="STRING"; break;
case java.sql.Types.OTHER:
type="STRING"; break;
case java.sql.Types.JAVA_OBJECT:
type="OBJECT"; break;
case java.sql.Types.DISTINCT:
type="STRING"; break;
case java.sql.Types.STRUCT:
type="STRING"; break;
case java.sql.Types.ARRAY:
type="ARRAY"; break;
case java.sql.Types.BLOB:
type="STRING"; break;
case java.sql.Types.CLOB:
type="STRING"; break;
case java.sql.Types.REF:
type="STRING"; break;
}
return type;
}
}
SerializableJSONArray Code, I'm writting this class code just for knowing whether its gonna work or not by the commenters :
public class SerializableJSONArray implements Serializable {
private transient JSONArray jsonArray;
public SerializableJSONArray(JSONArray jsonArray) {
this.jsonArray = jsonArray;
}
public JSONArray getJSONArray() {
return jsonArray;
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeObject(jsonArray.toString());
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException, JSONException {
ois.defaultReadObject();
jsonArray = new JSONArray((String) ois.readObject());
}
}
ResultSet is not serializable. You cannot convert (serialize) them directly to json.
You need first to extract the values you required from ResultSet to some serializable object before converting it into json.
Store your object in a map and then transform the map to json.
The example below does this and doesn't even need jackson. It uses javax.json
public static String toJSON(List<Map<String,String>> maps) {
return javax.json.Json.createArrayBuilder(maps).build().toString();
}

gson flat json to nested objects needs serializer/deserializer?

I have some JSON coming in (I don't have any control or ability to change the structure and/or naming within the JSON...important to keep in mind in this question) that has a "flat" structure similar to this:
{
"name": "...",
"email": "...",
"box_background_color": "...",
"box_border_color": "...",
"box_text_color": "...",
...
}
Now, I can just create a simple object that keeps everything flat, like so:
public class Settings {
#SerializedName("name")
private String _name;
#SerializedName("email")
private String _emailAddress;
#SerializedName("box_background_color")
private String _boxBackgroundColor;
#SerializedName("box_border_color")
private String _boxBorderColor;
#SerializedName("box_text_color")
private String _boxTextColor;
...
}
However, I want everything associated with box settings to be in it's own class (BoxSettings). This is more like what I want:
public class Settings {
#SerializedName("name")
private String _name;
#SerializedName("email")
private String _emailAddress;
private BoxSettings _boxSettings
...
}
public class BoxSettings {
#SerializedName("box_background_color")
private String _boxBackgroundColor;
#SerializedName("box_border_color")
private String _boxBorderColor;
#SerializedName("box_text_color")
private String _boxTextColor;
...
}
I know that if the JSON was structured such that the box settings were nested then it would be easy to accomplish what I want, however, I don't have the ability to change the structure of the JSON, so please don't suggest that (I would do it if I could).
My question is this: Is creating an entire TypeAdapter the only way to accomplish what I want or can I still accomplish most of this with annotations? If it is not the only way, how else can I accomplish this without changing the JSON at all?
The following is an example of what I mean by "creating an entire TypeAdapter":
public class SettingsTypeAdapter implements JsonDeserializer<Settings>, JsonSerializer<Settings> {
#Override
public JsonElement serialize(Settings src, Type typeOfSrc, JsonSerializationContext context) {
// Add _name
// Add _emailAddress
// Add BoxSettings._boxBackgroundColor
// Add BoxSettings._boxBorderColor
// Add BoxSettings._boxTextColor
return jsonElement;
}
#Override
public Settings deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
// Read _name
// Read _emailAddress
// Read BoxSettings._boxBackgroundColor
// Read BoxSettings._boxBorderColor
// Read BoxSettings._boxTextColor
return settings;
}
}
The TypeAdapter is not the only way, but in this case would be the best way since you can associate the adapter with a Gson instance (or whatever library you are using) and have all your mapping code there.
Another way is to use JAVA reflection. I've used a version of the below code in my projects before but never with JSON and never with nested objects (mostly when there was no other choice or if i wanted to map a SQL result set to a Java object without calling resultSet.get... a lot of times).
This will work in this case.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.json.JSONObject;
public class Main {
public static void main(String[] args) {
try {
String json = "{\"name\": \"test name\", \"email\": \"email#email.com\", \"box_background_color\": \"red\", \"box_border_color\": \"orange\", \"box_text_color\": \"white\", \"test3_var2\":3}";
JSONObject jsonObject = new JSONObject(json);
System.out.println(jsonObject);
System.out.println();
/*
* need to parse JSON into a map of String, Object
*/
Map<String, Object> mapAll = new HashMap<String, Object>();
Iterator<String> iter = jsonObject.keys();
while (iter.hasNext()) {
String key = (String) iter.next();
Object value = jsonObject.get(key);
mapAll.put(key, value);
System.out.println(key + "::::" + value);
}
System.out.println();
/*
* use the mapper to generate the objects
*/
MyMapper<TestClass1> myMapper = new MyMapper<TestClass1>();
TestClass1 result = myMapper.mapToObject(mapAll, TestClass1.class);
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class MyMapper<T> {
#SuppressWarnings("unchecked")
public T mapToObject(Map<String, Object> flatStructure, Class<T> objectClass) {
T result = null;
Field[] fields = null;
try {
// new base object
result = objectClass.newInstance();
// get all of its fields
fields = objectClass.getDeclaredFields();
for (Field field : fields) {
// normal variable
if (field.isAnnotationPresent(MyColumn.class)) {
String variableKey = field.getAnnotation(MyColumn.class).variableKey();
setJavaFieldValue(result, field.getName(), flatStructure.get(variableKey));
}
// variable that is an object and itself has to be mapped
else if (field.isAnnotationPresent(MyInnerColumn.class)) {
String startsWith = field.getAnnotation(MyInnerColumn.class).startsWith();
// reduce the map to only have attributes that are related to this field
Map<String, Object> reducedMap = reduceMap(startsWith, flatStructure);
// make sure that there are attributes for the inner object
if (reducedMap != null) {
// map the inner object
MyMapper<T> myMapper = new MyMapper<T>();
T t2 = myMapper.mapToObject(reducedMap, (Class<T>) field.getType());
// set the mapped object to the base objecct
setJavaFieldValue(result, field.getName(), t2);
}
} else {
// no annotation on the field so ignored
}
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private Map<String, Object> reduceMap(String startsWith, Map<String, Object> mapToReduce) {
Map<String, Object> result = new HashMap<String, Object>();
for (Map.Entry<String, Object> entry : mapToReduce.entrySet()) {
if (entry.getKey().toLowerCase().startsWith(startsWith.toLowerCase())) {
result.put(entry.getKey(), entry.getValue());
}
}
return result.size() == 0 ? null : result;
}
private void setJavaFieldValue(Object object, String fieldName, Object fieldValue) {
try {
Field field = object.getClass().getDeclaredField(fieldName);
boolean fieldAccess = field.isAccessible();
// make the field accessible
field.setAccessible(true);
field.set(object, fieldValue);
// put it back to the way it was
field.setAccessible(fieldAccess);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/*
* Annotation for a regular variable / field
*/
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#interface MyColumn {
// the variable's JSON key
String variableKey() default "";
}
/*
* Annotation for an inner / nested variable / field
*/
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#interface MyInnerColumn {
/*
* JSON keys that start with this string will be
* associated with this nested field
*/
String startsWith() default "";
}
class TestClass1 {
#MyColumn(variableKey = "name")
private String _name;
#MyColumn(variableKey = "email")
private String _emailAddress;
#MyInnerColumn(startsWith = "box_")
private TestClass2 innerClass;
#MyInnerColumn(startsWith = "test3_")
private TestClass3 innerClass2;
#Override
public String toString() {
return "TestClass1 [_name=" + _name + ", _emailAddress=" + _emailAddress + ", innerClass=" + innerClass + ", innerClass2=" + innerClass2 + "]";
}
}
class TestClass2 {
#MyColumn(variableKey = "box_background_color")
private String _boxBackgroundColor;
#MyColumn(variableKey = "box_border_color")
private String _boxBorderColor;
#MyColumn(variableKey = "box_text_color")
private String _boxTextColor;
#Override
public String toString() {
return "TestClass2 [_boxBackgroundColor=" + _boxBackgroundColor + ", _boxBorderColor=" + _boxBorderColor
+ ", _boxTextColor=" + _boxTextColor + "]";
}
}
class TestClass3 {
#MyColumn(variableKey = "test3_var1")
private String _test3Var1;
#MyColumn(variableKey = "test3_var2")
private int _test3Var2;
#Override
public String toString() {
return "TestClass3 [_test3Var1=" + _test3Var1 + ", _test3Var2=" + _test3Var2 + "]";
}
}
Output
{"box_background_color":"red","box_text_color":"white","test3_var2":3,"name":"test name","email":"email#email.com","box_border_color":"orange"}
box_background_color::::red
box_text_color::::white
test3_var2::::3
name::::test name
email::::email#email.com
box_border_color::::orange
TestClass1 [_name=test name, _emailAddress=email#email.com, innerClass=TestClass2 [_boxBackgroundColor=red, _boxBorderColor=orange, _boxTextColor=white], innerClass2=TestClass3 [_test3Var1=null, _test3Var2=3]]

What's your most reused class?

Every programmer ends up with a set of utility classes after a while. Some of them are true programming pearls and they are reused in several of your projects. For example, in java:
class Separator {
private String separator;
private boolean called;
public Separator(String aSeparator) {
separator = aSeparator;
called = false;
}
#Override
public String toString() {
if (!called) {
called = true;
return "";
} else {
return separator;
}
}
}
and:
public class JoinHelper {
public static <T> String join(T... elements) {
return joinArray(" ", elements);
}
public static <T> String join(String separator, T... elements) {
return joinArray(separator, elements);
}
private static <T> String joinArray(String sep, T[] elements) {
StringBuilder stringBuilder = new StringBuilder();
Separator separator = new Separator(sep);
for (T element : elements) {
stringBuilder.append(separator).append(element);
}
return stringBuilder.toString();
}
}
What is your most reused class?
System.Object - almost all my types extend it.
A utility class that has logging and email functionality. An extensions class that contains extension methods. A reporting class that basically harness the reporting services web service and makes it easy to stream reports as excel, pdf, etc.
Examples...
1.) Utility Class (static)
public static void LogError(Exception ex)
{
EventLog log = new EventLog();
if (ex != null)
{
log.Source = ConfigurationManager.AppSettings["EventLog"].ToString();
StringBuilder sErrorMessage = new StringBuilder();
if (HttpContext.Current.Request != null && HttpContext.Current.Request.Url != null)
{
sErrorMessage.Append(HttpContext.Current.Request.Url.ToString() + System.Environment.NewLine);
}
sErrorMessage.Append(ex.ToString());
log.WriteEntry(sErrorMessage.ToString(), EventLogEntryType.Error);
}
}
2.) Extensions Class
public static IEnumerable<TSource> WhereIf<TSource>(this IEnumerable<TSource> source, bool condition, Func<TSource, bool> predicate)
{
if (condition)
return source.Where(predicate);
else
return source;
}
public static short getLastDayOfMonth(short givenMonth, short givenYear)
{
short lastDay = 31;
switch (givenMonth)
{
case 4:
case 6:
case 9:
case 11:
lastDay = 30;
break;
case 2:
if ((int)givenYear % 4 == 0)
{
lastDay = 29;
}
else
{
lastDay = 28;
}
break;
}
return lastDay;
}
Most reused but boring:
public static void handleException(Exception e) throws RuntimeException {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw new RuntimeException(e); //NOPMD
}
Less boring (also methods for building lists and sets):
/**
* Builds a Map that is based on the Bean List.
*
* #param items Bean List items
* #param keyField Bean Field that will be key of Map elements (not null)
* #return a Map that is based on the Bean List
*/
#SuppressWarnings("unchecked")
public static <T, K> Map<K, T> buildMapFromCollection(final Collection<T> items,
boolean linkedMap,
final String keyField,
final Class<K> keyType) {
if (items == null) {
return Collections.emptyMap();
}
if (keyField == null) {
throw new IllegalArgumentException("KeyField is null");
}
final Map<K, T> result;
if (linkedMap) {
result = new LinkedHashMap<K, T>();
} else {
result = new HashMap<K, T>();
}
BeanMapper mapper = null;
for (final T item : items) {
if (mapper == null) {
mapper = new BeanMapper(item.getClass());
}
final K key = (K) mapper.getFieldValue(item, keyField);
result.put(key, item);
}
return result;
}
Logger class: Which logs the flow of control in a log file.
Configuration Reader/Setter: which reads the configuration from ini/xml file and sets the environment of the application
Most reused? Hmmm...
boost::shared_ptr<> with boost::weak_ptr<>
probably most reused (also probably most bang-for-buck ratio)
Globals
Just a simple class with static DBConnString, and a few other app wide settings.
Have reused the simple file in about 2 dozen projects since working with .Net
A ConcurrentDictionary I wrote, which I now seem to use everywhere (I write lots of parallel programs)