package org.mindinformatics.domeo.persistence;
import java.io.*;
import java.util.*;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.ContentHandler;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
/**
* Sax-like JSON parser
* Slightly modified from: http://code.google.com/p/json-simple/wiki/DecodingExamples
* Original License: Apache 2.0 http://code.google.com/p/json-simple/
*/
class Transformer implements ContentHandler {
/** Unique replacement character string for colons found in JSON object key */
final static String COLON_REPLACEMENT = "_!DOMEO_NS!_";
/** Stack of transformed JSON objects */
private Stack<Object> valueStack;
/**
* Retrieve the transformed JSON document from the stack.
* @return transformed document or null
*/
public Object getResult(){
if(valueStack == null || valueStack.size() == 0) {
return null;
}
return valueStack.peek();
}
/**
* End of JSON array (right square bracket)
*/
public boolean endArray () throws ParseException, IOException {
trackBack();
return true;
}
/**
* End of JSON document detected
*/
public void endJSON () throws ParseException, IOException {}
/**
* End of JSON object (right brace)
*/
public boolean endObject () throws ParseException, IOException {
trackBack();
return true;
}
/**
* If this is the end of a JSON object, then we pop off one of the duplicate objects
*/
private void trackBack(){
if(valueStack.size() > 1) {
Object value = valueStack.pop();
Object prev = valueStack.peek();
if(prev instanceof String) {
valueStack.push(value);
}
}
}
/**
* Add the new value on to the stack.
* If the previous item on the stack is a list, then append the value to the list
* @param value
*/
private void consumeValue(Object value) {
if (valueStack.size()==0){
valueStack.push(value);
}
else {
Object prev = valueStack.peek();
if (prev instanceof List) {
@SuppressWarnings("unchecked")
List<Object> array = (List<Object>)prev;
array.add(value);
}
else {
valueStack.push(value);
}
}
}
/**
* The value part of the JSON key-value pair
* We don't care if this has embedded colon characters
*/
public boolean primitive (Object value) throws ParseException, IOException {
consumeValue(value);
return true;
}
/**
* Either we have reached the end of a JSON object or we have just called
* primitive above to push the value of the JSON key-value pair on to the
* stack.
*
* Pop the key-value items off the stack and add them as children of the
* previous stack entry (the parents)
*/
public boolean endObjectEntry () throws ParseException, IOException {
Object value = valueStack.pop();
Object key = valueStack.pop();
@SuppressWarnings("unchecked")
Map<Object, Object> parent = (Map<Object, Object>)valueStack.peek();
parent.put(key, value);
return true;
}
/**
* Start of JSON array (left square bracket detected)
*/
public boolean startArray () throws ParseException, IOException {
@SuppressWarnings("rawtypes")
List array = new JSONArray();
consumeValue(array);
valueStack.push(array);
return true;
}
/**
* Start of JSON document detected
*/
public void startJSON () throws ParseException, IOException {
valueStack = new Stack<Object>();
}
/**
* Start of JSON object (left brace) detected
*/
public boolean startObject () throws ParseException, IOException {
@SuppressWarnings("rawtypes")
Map object = new JSONObject();
consumeValue(object);
valueStack.push(object);
return true;
}
/**
* Found the key of a JSON object
* Replace all colons in key before placing on stack
*/
public boolean startObjectEntry (String key) throws ParseException, IOException {
key = key.replaceAll(":", COLON_REPLACEMENT);
valueStack.push(key);
return true;
}
/**
* Demonstrate parser with namespace encoding and then decoding
*
* The Domeo document key-value pairs may include namespaces. The colon
* character in the namespace is incompatible with ElasticSearch and must
* be removed before the document is indexed.
*/
public static void main(String[] args) {
// Sample json with namespace key
String jsonText = "{\"ns:first\": \"peanuts\", \"second\": [4, 5, 6], \"third\": 789}";
// 1. Demonstrate parse and replace colons in namespace
JSONParser parser = new JSONParser();
Transformer transformer = new Transformer();
try {
parser.parse(jsonText, transformer);
} catch (ParseException e) {
e.printStackTrace();
}
Object value = transformer.getResult();
System.out.println("Final value:\n" + value);
// 2. Return the colons to the namespace form
System.out.println(value.toString().replaceAll(COLON_REPLACEMENT, ":"));
}
}