/* * Copyright 2009-2012 Alan Kennedy * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xhaus.jyson; import org.python.core.*; public class JysonEncoder { /** Controls whether the Jyson encoder emits unicode or ascii strings */ public boolean emit_ascii = false; protected JysonEncoder ( ) { } // // This code has been pinched from the jython source base: org.core.python.PyString // And modified to // 1. Always use double quotes, never single // 2. Output control characters as unicode escapes, not \x escapes // private static char[] hexdigit = "0123456789ABCDEF".toCharArray(); protected void append_json_string_repr ( StringBuffer buf, String str ) { int size = str.length(); StringBuffer v = new StringBuffer(str.length()); char quote = '"'; buf.append(quote); for (int i = 0; size-- > 0; ) { int ch = str.charAt(i++); /* Escape quotes */ if (ch == quote || ch == '\\') { buf.append('\\'); buf.append((char) ch); } else if (ch == '\n') buf.append("\\n"); else if (ch == '\t') buf.append("\\t"); else if (ch == '\b') buf.append("\\b"); else if (ch == '\f') buf.append("\\f"); else if (ch == '\r') buf.append("\\r"); else if (ch < ' ' || (ch >= 127 && emit_ascii) || (ch >= Character.MIN_SURROGATE && ch <= Character.MAX_SURROGATE)) { /* Map control and non ascii characters to '\\uxxxx' */ buf.append("\\u"); buf.append(hexdigit[(ch >> 12) & 0xf]); buf.append(hexdigit[(ch >> 8) & 0xf]); buf.append(hexdigit[(ch >> 4) & 0xf]); buf.append(hexdigit[ch & 15]); } /* Copy everything else as-is */ else buf.append((char) ch); } buf.append(quote); } protected void append_json_map_repr ( StringBuffer buf, PyObject map, PyList keys ) throws JSONEncodeError { int num_keys = keys.__len__(); buf.append('{'); for (int ix = 0 ; ix < num_keys ; ix++) { PyObject k = keys.__getitem__(ix); if (!(k instanceof PyString)) throw new JSONEncodeError(((PyType)k.fastGetClass()).fastGetName()+" objects are not permitted as JSON object keys."); append_json_string_repr(buf, ((PyString)k).toString()); buf.append(':'); buf.append(json_repr(map.__finditem__(k))); if (ix < num_keys-1) buf.append(','); } buf.append('}'); } protected void append_json_string_map_repr ( StringBuffer buf, PyStringMap map ) throws JSONEncodeError { append_json_map_repr(buf, map, map.keys()); } protected void append_json_dictionary_repr ( StringBuffer buf, PyDictionary map ) throws JSONEncodeError { append_json_map_repr(buf, map, map.keys()); } protected void append_json_sequence_repr ( StringBuffer buf, PySequence sequence ) throws JSONEncodeError { int num_items = sequence.__len__(); buf.append('['); for (int ix = 0 ; ix < num_items ; ix++) { buf.append(json_repr(sequence.__getitem__(ix))); if (ix < num_items-1) buf.append(','); } buf.append(']'); } public void append_json_repr ( StringBuffer buf, PyObject py_obj ) throws JSONEncodeError { if (py_obj instanceof PyString) append_json_string_repr(buf, ((PyString)py_obj).toString()); // Must test for PyBoolean before PyInteger because former is a subclass of latter. else if (py_obj instanceof PyBoolean) buf.append(((PyBoolean)py_obj).getBooleanValue() ? "true" : "false"); else if (py_obj instanceof PyInteger) buf.append(Integer.toString(((PyInteger)py_obj).getValue())); else if (py_obj instanceof PyLong) { String repr = ((PyLong)py_obj).__repr__().toString(); buf.append(repr.substring(0, repr.length()-1)); } else if (py_obj instanceof PyFloat) buf.append(Double.toString(((PyFloat)py_obj).getValue())); else if (py_obj instanceof PyStringMap) append_json_string_map_repr(buf, (PyStringMap)py_obj); else if (py_obj instanceof PyDictionary) append_json_dictionary_repr(buf, (PyDictionary)py_obj); else if (py_obj instanceof PySequence) append_json_sequence_repr(buf, (PySequence)py_obj); else if (py_obj instanceof PyNone) buf.append("null"); else if (py_obj.__findattr__("__json__") != null && py_obj.__findattr__("__json__").isCallable()) buf.append(((PyMethod)py_obj.__findattr__("__json__")).__call__().toString()); else throw new JSONEncodeError("Python '"+((PyType)py_obj.fastGetClass()).fastGetName() +"' object '"+py_obj.__repr__()+"' is not encodable in JSON"); } public String json_repr ( PyObject py_obj ) throws JSONEncodeError { StringBuffer buf = new StringBuffer(); append_json_repr(buf, py_obj); return buf.toString(); } }