changeset 381:bed51de8ae58

Added JSON parser from Artefact Server NG to bridge from JSON to XML speaking services. artifacts/trunk@4270 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Wed, 18 Apr 2012 17:10:10 +0000
parents 9798e4d83681
children a94bc2491b41
files ChangeLog artifacts-common/src/main/java/de/intevation/artifacts/common/utils/JSON.java artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java
diffstat 3 files changed, 563 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Apr 03 11:15:10 2012 +0000
+++ b/ChangeLog	Wed Apr 18 17:10:10 2012 +0000
@@ -1,3 +1,13 @@
+2012-04-18	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/JSON.java:
+	  New. JSON parser from Artefact Server NG (which relies on JSON more heavily).
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java:
+	  Added method to convert JSON to XML. This is needed by the client which
+	  internally uses JSON to talk to the server which services only understands
+	  XML atm.
+	
 2012-04-04	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
 
 	Modified services so that they are now able to return more than just
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/JSON.java	Wed Apr 18 17:10:10 2012 +0000
@@ -0,0 +1,436 @@
+package de.intevation.artifacts.common.utils;
+
+import java.util.Map;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import java.io.IOException;
+import java.io.PushbackInputStream;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.ByteArrayInputStream;
+
+import java.nio.charset.Charset;
+import java.nio.charset.UnsupportedCharsetException;
+
+public final class JSON
+{
+    private JSON() {
+    }
+
+    private static final boolean isDigit(int c) {
+        return c >= '0' && c <= '9';
+    }
+
+    public static final boolean isWhitespace(int c) {
+        return c == ' '  || c == '\n' || c == '\r' 
+            || c == '\t' || c == '\f';
+    }
+
+    private static final void match(int c, int x) throws IOException {
+        if (c != x) {
+            throw new IOException(
+                "Expecting '" + (char)c + "' found '" + (char)x + "'");
+        }
+    }
+
+    private static final int eof(InputStream in)
+    throws IOException
+    {
+        int c = in.read();
+        if (c == -1) {
+            throw new IOException("EOF unexpected.");
+        }
+        return c;
+    }
+
+    private static final int whitespace(InputStream in)
+    throws IOException
+    {
+        int c;
+        while (isWhitespace(c = eof(in)));
+        return c;
+    }
+
+    private static final int parseHex(String hex) throws IOException {
+        try {
+            return Integer.parseInt(hex, 16);
+        }
+        catch (NumberFormatException nfe) {
+            throw new IOException("'" + hex + "' is not a hex string.");
+        }
+    }
+
+    public static final String jsonString(String string) {
+        StringBuilder sb = new StringBuilder(string.length()+2);
+
+        sb.append('"');
+
+        for (int i = 0, N = string.length(); i < N; ++i) {
+            char c = string.charAt(i);
+            switch (c) {
+                case '"':  sb.append("\\\""); break;
+                case '\t': sb.append("\\t"); break;
+                case '\r': sb.append("\\r"); break;
+                case '\n': sb.append("\\n"); break;
+                case '\b': sb.append("\\b"); break;
+                case '\f': sb.append("\\f"); break;
+                default:
+                    if (c >= 128) {
+                        sb.append("\\u");
+                        String hex = Integer.toHexString((int)c);
+                        for (int j = 4-hex.length(); j > 0; --j) {
+                            sb.append('0');
+                        }
+                        sb.append(hex);
+                    }
+                    else {
+                        sb.append(c);
+                    }
+            }
+        }
+
+        sb.append('"');
+
+        return sb.toString();
+    }
+
+
+    public static void write(PrintWriter out, Map<String, Object> map) {
+        writeObject(out, map);
+    }
+
+    private static void writeValue(PrintWriter out, Object value) {
+        if (value instanceof Map) {
+            writeObject(out, (Map)value);
+        }
+        else if (value instanceof List) {
+            writeList(out, (List)value);
+        }
+        else if (value instanceof Number) {
+            out.print(value);
+        }
+        else if (value instanceof Boolean) {
+            out.print(((Boolean)value) ? "true" : "false");
+        }
+        else if (value == null) {
+            out.print("null");
+        }
+        else {
+            out.print(jsonString(value.toString()));
+        }
+    }
+
+    private static void writeObject(PrintWriter out, Map map) {
+
+        out.print('{');
+        Iterator iter = map.entrySet().iterator();
+        while (iter.hasNext()) {
+            Map.Entry entry = (Map.Entry)iter.next();
+            out.print(jsonString(entry.getKey().toString()));
+            out.print(':');
+            writeValue(out, entry.getValue());
+            if (iter.hasNext()) {
+                out.print(',');
+            }
+        }
+        out.print('}');
+    }
+
+    private static void writeList(PrintWriter out, List list) {
+        out.print('[');
+        Iterator iter = list.iterator();
+        while (iter.hasNext()) {
+            writeValue(out, iter.next());
+            if (iter.hasNext()) {
+                out.print(',');
+            }
+        }
+        out.print(']');
+    }
+
+    public static Map<String, Object> parse(String in) 
+    throws IOException 
+    {
+        return parse(asInputStream(in));
+    }
+
+    private static InputStream asInputStream(String in) {
+        byte [] bytes;
+        try {
+            bytes = in.getBytes(Charset.forName("US-ASCII"));
+        }
+        catch (UnsupportedCharsetException uce) {
+            // Should not happen.
+            bytes = in.getBytes();
+        }
+        return new ByteArrayInputStream(bytes);
+    }
+
+    public static Map<String, Object> parse(InputStream in) 
+    throws IOException 
+    {
+        return parseObject(new PushbackInputStream(in, 1));
+    }
+
+    public static Map<String, Object> parse(PushbackInputStream in) 
+    throws IOException 
+    {
+        return parseObject(in);
+    }
+
+    private static final String parseString(
+        PushbackInputStream in
+    )
+    throws IOException
+    {
+        StringBuilder sb = new StringBuilder();
+
+        int mode = 0;
+
+        char [] hex = new char[4];
+
+        match('"', eof(in));
+
+        OUT: for (int c = eof(in);; c = eof(in)) {
+
+            switch (mode) {
+                case 0:
+                    if (c == '"') {
+                        break OUT;
+                    }
+                    if (c == '\\') {
+                        mode = 1;
+                    }
+                    else {
+                        sb.append((char)c);
+                    }
+                    break;
+                case 1:
+                    switch (c) {
+                        case 'u':
+                            mode = 2;
+                            continue;
+                        case 'b':
+                            sb.append('\b');
+                            break;
+                        case 'f':
+                            sb.append('\f');
+                            break;
+                        case 'n':
+                            sb.append('\n');
+                            break;
+                        case 'r':
+                            sb.append('\r');
+                            break;
+                        case 't':
+                            sb.append('\t');
+                            break;
+                        default:
+                            sb.append((char)c);
+                    }
+                    mode = 0;
+                    break;
+                case 2:
+                    hex[0] = (char)c;
+                    mode = 3;
+                    break;
+                case 3:
+                    hex[1] = (char)c;
+                    mode = 4;
+                    break;
+                case 4:
+                    hex[2] = (char)c;
+                    mode = 5;
+                    break;
+                case 5:
+                    hex[3] = (char)c;
+                    sb.append((char)parseHex(new String(hex)));
+                    mode = 0;
+                    break;
+            }
+        }
+        return sb.toString();
+    }
+
+    private static final Boolean parseTrue(InputStream in)
+    throws IOException
+    { 
+        match('t', eof(in));
+        match('r', eof(in));
+        match('u', eof(in));
+        match('e', eof(in));
+        return Boolean.TRUE;
+    }
+
+    private static final Boolean parseFalse(InputStream in)
+    throws IOException
+    { 
+        match('f', eof(in));
+        match('a', eof(in));
+        match('l', eof(in));
+        match('s', eof(in));
+        match('e', eof(in));
+        return Boolean.FALSE;
+    }
+
+    private static final Object parseNull(InputStream in)
+    throws IOException
+    { 
+        match('n', eof(in));
+        match('u', eof(in));
+        match('l', eof(in));
+        match('l', eof(in));
+        return null;
+    }
+
+    private static final Number parseNumber(PushbackInputStream in)
+    throws IOException
+    {
+        StringBuilder sb = new StringBuilder();
+
+        boolean isInteger = true;
+
+        int c;
+        OUT: for (;;) {
+            switch (c = eof(in)) {
+                case '0': case '1': case '2': case '3': case '4':
+                case '5': case '6': case '7': case '8': case '9':
+                case '-': case '+':
+                    sb.append((char)c);
+                    break;
+                case '.': case 'e': case 'E':
+                    isInteger = false;
+                    sb.append((char)c);
+                    break;
+                default:
+                    in.unread(c);
+                    break OUT;
+            }
+        }
+
+        try {
+            if (isInteger) {
+                return sb.length() > 9
+                    ? (Number)Long   .valueOf(sb.toString())
+                    : (Number)Integer.valueOf(sb.toString());
+            }
+            return (Number)Double.valueOf(sb.toString());
+        }
+        catch (NumberFormatException nfe) {
+            throw new IOException("Not a number '" + sb + "'");
+        }
+    }
+
+    private static List<Object> parseList(PushbackInputStream in)
+    throws IOException
+    {
+        List<Object> list = new ArrayList<Object>();
+        match('[', whitespace(in));
+        int c = whitespace(in);
+        if (c == ']') {
+            return list;
+        }
+
+        for (;; c = whitespace(in)) {
+            Object value;
+            in.unread(c);
+            switch (c) {
+                case '{':
+                    value = parseObject(in);
+                    break;
+                case '[':
+                    value = parseList(in);
+                    break;
+                case '"':
+                    value = parseString(in);
+                    break;
+                case 't':
+                    value = parseTrue(in);
+                    break;
+                case 'f':
+                    value = parseFalse(in);
+                    break;
+                case 'n':
+                    value = parseNull(in);
+                    break;
+                default:
+                    value = parseNumber(in);
+            }
+            list.add(value);
+
+            if ((c = whitespace(in)) == ']') break;
+            match(',', c);
+        }
+        return list;
+    }
+
+    private static void parsePair(
+        PushbackInputStream in,
+        Map<String, Object> pairs
+    )
+    throws IOException
+    {
+        in.unread(whitespace(in));
+        String string = parseString(in);
+        match(':', whitespace(in));
+
+        Object value;
+
+        int c = whitespace(in);
+        in.unread(c);
+        switch (c) {
+            case '{':
+                value = parseObject(in);
+                break;
+            case '[':
+                value = parseList(in);
+                break;
+            case '"':
+                value = parseString(in);
+                break;
+            case 't':
+                value = parseTrue(in);
+                break;
+            case 'f':
+                value = parseFalse(in);
+                break;
+            case 'n':
+                value = parseNull(in);
+                break;
+            default:
+                value = parseNumber(in);
+        }
+        pairs.put(string, value);
+    }
+
+    private static Map<String, Object> parseObject(PushbackInputStream in) 
+    throws IOException
+    {
+        Map<String, Object> pairs = new LinkedHashMap<String, Object>();
+
+        int c = whitespace(in);
+        match('{', c);
+
+        if ((c = whitespace(in)) == '}') {
+            return pairs;
+        }
+
+        in.unread(c);
+
+        for (;;) {
+            parsePair(in, pairs);
+
+            if ((c = whitespace(in)) == '}') {
+                break;
+            }
+
+            if (c == '}') break;
+            match(',', c);
+        }
+
+        return pairs;
+    }
+}
--- a/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java	Tue Apr 03 11:15:10 2012 +0000
+++ b/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java	Wed Apr 18 17:10:10 2012 +0000
@@ -8,7 +8,10 @@
 
 package de.intevation.artifacts.common.utils;
 
+import java.util.List;
+import java.util.ArrayList;
 import java.util.Map;
+import java.util.LinkedHashMap;
 import java.util.zip.GZIPInputStream;
 import java.util.zip.GZIPOutputStream;
 
@@ -464,5 +467,119 @@
         }
         return null;
     }
+
+    private static class BuildResult {
+        List<Node>          children;
+        Map<String, String> attributes;
+        BuildResult() {
+            children   = new ArrayList<Node>();
+            attributes = new LinkedHashMap<String, String>();
+        }
+
+        void setAttributes(Element element) {
+            for (Map.Entry<String, String> entry: attributes.entrySet()) {
+                element.setAttribute(entry.getKey(), entry.getValue());
+            }
+        }
+        void add(Node node) {
+            children.add(node);
+        }
+
+        void add(String key, Object value) {
+            attributes.put(key, value != null ? value.toString() : "null");
+        }
+    } // class BuildResult
+
+    private static BuildResult recursiveBuild(
+        List     list,
+        Document document
+    ) {
+        BuildResult result = new BuildResult();
+        for (Object entry: list) {
+            if (entry instanceof Map) {
+                Element element = document.createElement("map");
+                BuildResult subResult = recursiveBuild(
+                    (Map<String, Object>)entry, document);
+                subResult.setAttributes(element);
+                result.add(element);
+            }
+            else if (entry instanceof List) {
+                Element element = document.createElement("list");
+                BuildResult subResult = recursiveBuild((List)entry, document);
+                subResult.setAttributes(element);
+                result.add(element);
+            }
+            else {
+                Element element = document.createElement("entry");
+                element.setAttribute(
+                    "value",
+                    entry != null ? entry.toString() : "null");
+            }
+        }
+        return result;
+    }
+
+    private static BuildResult recursiveBuild(
+        Map<String, Object> map,
+        Document            document
+    ) {
+        BuildResult result = new BuildResult();
+
+        List<Node> nodes = new ArrayList<Node>();
+        for (Map.Entry<String, Object> entry: map.entrySet()) {
+            Object value = entry.getValue();
+            if (value instanceof Map) {
+                Element element = document.createElement(entry.getKey());
+                BuildResult subResult = recursiveBuild(
+                    (Map<String, Object>)value, document);
+                subResult.setAttributes(element);
+                result.add(element);
+            }
+            else if (value instanceof List) {
+                Element element = document.createElement(entry.getKey());
+                BuildResult subResult = recursiveBuild((List)value, document);
+                subResult.setAttributes(element);
+                result.add(element);
+            }
+            else {
+                result.add(entry.getKey(), value);
+            }
+        }
+        return result;
+    }
+
+    public static Document jsonToXML(String input) {
+        Document document = newDocument();
+
+        if (document == null) {
+            return null;
+        }
+
+        Map<String, Object> map;
+        try {
+            map = JSON.parse(input);
+        }
+        catch (IOException ioe) {
+            logger.error(ioe);
+            return null;
+        }
+
+        BuildResult roots = recursiveBuild(map, document);
+
+        int N = roots.children.size();
+
+        if (N == 1) {
+            document.appendChild(roots.children.get(0));
+        }
+        else if (N > 1) {
+            Node root = document.createElement("root");
+            for (int i = 0; i < N; ++i) {
+                root.appendChild(roots.children.get(i));
+            }
+            document.appendChild(root);
+        }
+
+        return document;
+    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org