diff --git a/src/main/java/ch/dissem/msgpack/types/MPArray.java b/src/main/java/ch/dissem/msgpack/types/MPArray.java index 6f8c82e..f83cc39 100644 --- a/src/main/java/ch/dissem/msgpack/types/MPArray.java +++ b/src/main/java/ch/dissem/msgpack/types/MPArray.java @@ -25,6 +25,7 @@ public class MPArray implements MPType>, List { this.array = array; } + @SafeVarargs public MPArray(T... objects) { this.array = Arrays.asList(objects); } @@ -179,17 +180,29 @@ public class MPArray implements MPType>, List { @Override public String toString() { + return toJson(); + } + + @Override + public String toJson() { + return toJson(""); + } + + String toJson(String indent) { StringBuilder result = new StringBuilder(); - result.append('['); + result.append("[\n"); Iterator iterator = array.iterator(); + String indent2 = indent + " "; while (iterator.hasNext()) { T item = iterator.next(); - result.append(item.toString()); + result.append(indent2); + result.append(Utils.toJson(item, indent2)); if (iterator.hasNext()) { - result.append(", "); + result.append(','); } + result.append('\n'); } - result.append(']'); + result.append("]"); return result.toString(); } diff --git a/src/main/java/ch/dissem/msgpack/types/MPBinary.java b/src/main/java/ch/dissem/msgpack/types/MPBinary.java index 600c306..163d724 100644 --- a/src/main/java/ch/dissem/msgpack/types/MPBinary.java +++ b/src/main/java/ch/dissem/msgpack/types/MPBinary.java @@ -52,7 +52,12 @@ public class MPBinary implements MPType { @Override public String toString() { - return null; // TODO base64 + return toJson(); + } + + @Override + public String toJson() { + return Utils.base64(value); } public static class Unpacker implements MPType.Unpacker { diff --git a/src/main/java/ch/dissem/msgpack/types/MPBoolean.java b/src/main/java/ch/dissem/msgpack/types/MPBoolean.java index a9e4f53..fa95460 100644 --- a/src/main/java/ch/dissem/msgpack/types/MPBoolean.java +++ b/src/main/java/ch/dissem/msgpack/types/MPBoolean.java @@ -48,6 +48,11 @@ public class MPBoolean implements MPType { return String.valueOf(value); } + @Override + public String toJson() { + return String.valueOf(value); + } + public static class Unpacker implements MPType.Unpacker { public boolean is(int firstByte) { diff --git a/src/main/java/ch/dissem/msgpack/types/MPDouble.java b/src/main/java/ch/dissem/msgpack/types/MPDouble.java index 69fb041..7407a23 100644 --- a/src/main/java/ch/dissem/msgpack/types/MPDouble.java +++ b/src/main/java/ch/dissem/msgpack/types/MPDouble.java @@ -46,6 +46,11 @@ public class MPDouble implements MPType { return String.valueOf(value); } + @Override + public String toJson() { + return String.valueOf(value); + } + public static class Unpacker implements MPType.Unpacker { public boolean is(int firstByte) { return firstByte == 0xCB; diff --git a/src/main/java/ch/dissem/msgpack/types/MPFloat.java b/src/main/java/ch/dissem/msgpack/types/MPFloat.java index 965cb72..8d31318 100644 --- a/src/main/java/ch/dissem/msgpack/types/MPFloat.java +++ b/src/main/java/ch/dissem/msgpack/types/MPFloat.java @@ -46,6 +46,11 @@ public class MPFloat implements MPType { return String.valueOf(value); } + @Override + public String toJson() { + return String.valueOf(value); + } + public static class Unpacker implements MPType.Unpacker { public boolean is(int firstByte) { return firstByte == 0xCA; diff --git a/src/main/java/ch/dissem/msgpack/types/MPInteger.java b/src/main/java/ch/dissem/msgpack/types/MPInteger.java index 7cc17a1..c8bf051 100644 --- a/src/main/java/ch/dissem/msgpack/types/MPInteger.java +++ b/src/main/java/ch/dissem/msgpack/types/MPInteger.java @@ -78,6 +78,11 @@ public class MPInteger implements MPType { return String.valueOf(value); } + @Override + public String toJson() { + return String.valueOf(value); + } + public static class Unpacker implements MPType.Unpacker { public boolean is(int firstByte) { switch (firstByte) { diff --git a/src/main/java/ch/dissem/msgpack/types/MPMap.java b/src/main/java/ch/dissem/msgpack/types/MPMap.java index 7424063..f8e8d3d 100644 --- a/src/main/java/ch/dissem/msgpack/types/MPMap.java +++ b/src/main/java/ch/dissem/msgpack/types/MPMap.java @@ -123,22 +123,34 @@ public class MPMap implements MPType> iterator = map.entrySet().iterator(); + String indent2 = indent + " "; while (iterator.hasNext()) { Map.Entry item = iterator.next(); - result.append(item.getKey().toString()); + result.append(indent2); + result.append(Utils.toJson(item.getKey(), indent2)); result.append(": "); - result.append(item.getValue().toString()); + result.append(Utils.toJson(item.getValue(), indent2)); if (iterator.hasNext()) { - result.append(", "); + result.append(','); } + result.append('\n'); } - result.append('}'); + result.append(indent).append("}"); return result.toString(); } + @Override + public String toJson() { + return toJson(""); + } + public static class Unpacker implements MPType.Unpacker { private final Reader reader; diff --git a/src/main/java/ch/dissem/msgpack/types/MPNil.java b/src/main/java/ch/dissem/msgpack/types/MPNil.java index d77af46..5243115 100644 --- a/src/main/java/ch/dissem/msgpack/types/MPNil.java +++ b/src/main/java/ch/dissem/msgpack/types/MPNil.java @@ -33,6 +33,11 @@ public class MPNil implements MPType { return "null"; } + @Override + public String toJson() { + return "null"; + } + public static class Unpacker implements MPType.Unpacker { public boolean is(int firstByte) { diff --git a/src/main/java/ch/dissem/msgpack/types/MPString.java b/src/main/java/ch/dissem/msgpack/types/MPString.java index 3fc17c3..0b0db09 100644 --- a/src/main/java/ch/dissem/msgpack/types/MPString.java +++ b/src/main/java/ch/dissem/msgpack/types/MPString.java @@ -99,6 +99,50 @@ public class MPString implements MPType, CharSequence { return value; } + @Override + public String toJson() { + StringBuilder result = new StringBuilder(value.length() + 4); + result.append('"'); + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + switch (c) { + case '\\': + case '"': + case '/': + result.append('\\').append(c); + break; + case '\b': + result.append("\\b"); + break; + case '\t': + result.append("\\t"); + break; + case '\n': + result.append("\\n"); + break; + case '\f': + result.append("\\f"); + break; + case '\r': + result.append("\\r"); + break; + default: + if (c < ' ') { + result.append("\\u"); + String hex = Integer.toHexString(c); + for (int j = 0; j - hex.length() < 4; j++) { + result.append('0'); + } + result.append(hex); + } else { + result.append(c); + } + } + } + result.append('"'); + return result.toString(); + } + public static class Unpacker implements MPType.Unpacker { public boolean is(int firstByte) { return firstByte == STR8_PREFIX || firstByte == STR16_PREFIX || firstByte == STR32_PREFIX diff --git a/src/main/java/ch/dissem/msgpack/types/MPType.java b/src/main/java/ch/dissem/msgpack/types/MPType.java index 9136223..2e62fe5 100644 --- a/src/main/java/ch/dissem/msgpack/types/MPType.java +++ b/src/main/java/ch/dissem/msgpack/types/MPType.java @@ -17,4 +17,6 @@ public interface MPType { T getValue(); void pack(OutputStream out) throws IOException; + + String toJson(); } diff --git a/src/main/java/ch/dissem/msgpack/types/Utils.java b/src/main/java/ch/dissem/msgpack/types/Utils.java index 641076b..b90b610 100644 --- a/src/main/java/ch/dissem/msgpack/types/Utils.java +++ b/src/main/java/ch/dissem/msgpack/types/Utils.java @@ -5,6 +5,8 @@ import java.io.InputStream; import java.nio.ByteBuffer; class Utils { + private static final char[] BASE64_CODES = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".toCharArray(); + /** * Returns a {@link ByteBuffer} containing the next count bytes from the {@link InputStream}. */ @@ -20,4 +22,49 @@ class Utils { } return ByteBuffer.wrap(result); } + + /** + * Helper method to decide which types support extra indention (for pretty printing JSON) + */ + static String toJson(MPType type, String indent) { + if (type instanceof MPMap) { + return ((MPMap) type).toJson(indent); + } + if (type instanceof MPArray) { + return ((MPArray) type).toJson(indent); + } + return type.toJson(); + } + + /** + * Slightly improved code from https://en.wikipedia.org/wiki/Base64 + */ + static String base64(byte[] data) { + StringBuilder result = new StringBuilder((data.length * 4) / 3 + 3); + int b; + for (int i = 0; i < data.length; i += 3) { + b = (data[i] & 0xFC) >> 2; + result.append(BASE64_CODES[b]); + b = (data[i] & 0x03) << 4; + if (i + 1 < data.length) { + b |= (data[i + 1] & 0xF0) >> 4; + result.append(BASE64_CODES[b]); + b = (data[i + 1] & 0x0F) << 2; + if (i + 2 < data.length) { + b |= (data[i + 2] & 0xC0) >> 6; + result.append(BASE64_CODES[b]); + b = data[i + 2] & 0x3F; + result.append(BASE64_CODES[b]); + } else { + result.append(BASE64_CODES[b]); + result.append('='); + } + } else { + result.append(BASE64_CODES[b]); + result.append("=="); + } + } + + return result.toString(); + } } diff --git a/src/test/resources/demo.json b/src/test/resources/demo.json index b091f84..6118e1d 100644 --- a/src/test/resources/demo.json +++ b/src/test/resources/demo.json @@ -1 +1,4 @@ -{"compact": true, "schema": 0} \ No newline at end of file +{ + "compact": true, + "schema": 0 +} \ No newline at end of file