1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.graalvm.compiler.salver.serialize; 24 25 import java.io.IOException; 26 import java.util.List; 27 import java.util.Map; 28 29 import org.graalvm.compiler.salver.writer.DumpWriter; 30 31 public class JSONSerializer extends AbstractSerializer { 32 33 public static final String MEDIA_TYPE = "application/json"; 34 public static final String FILE_EXTENSION = "json"; 35 36 public JSONSerializer() { 37 } 38 39 public JSONSerializer(DumpWriter writer) { 40 super(writer); 41 } 42 43 @Override 44 public JSONSerializer serialize(Object obj) throws IOException { 45 if (writer != null) { 46 writer.write(appendValue(new StringBuilder(), obj).append('\n')); 47 } 48 return this; 49 } 50 51 public static StringBuilder stringify(StringBuilder sb, Object obj) { 52 return appendValue(sb, obj); 53 } 54 55 public static String stringify(Object obj) { 56 return appendValue(new StringBuilder(), obj).toString(); 57 } 58 59 public static String getMediaType() { 60 return MEDIA_TYPE; 61 } 62 63 public static String getFileExtension() { 64 return FILE_EXTENSION; 65 } 66 67 @SuppressWarnings("unchecked") 68 private static StringBuilder appendValue(StringBuilder sb, Object val) { 69 if (val instanceof Map<?, ?>) { 70 return appendDict(sb, (Map<Object, Object>) val); 71 } 72 if (val instanceof List<?>) { 73 return appendList(sb, (List<Object>) val); 74 } 75 if (val instanceof byte[]) { 76 return appendByteArray(sb, (byte[]) val); 77 } 78 if (val instanceof Number) { 79 return sb.append(val); 80 } 81 if (val instanceof Boolean) { 82 return sb.append(val); 83 } 84 if (val == null) { 85 return sb.append("null"); 86 } 87 return appendString(sb, String.valueOf(val)); 88 } 89 90 private static StringBuilder appendDict(StringBuilder sb, Map<Object, Object> dict) { 91 sb.append('{'); 92 boolean comma = false; 93 for (Map.Entry<Object, Object> entry : dict.entrySet()) { 94 if (comma) { 95 sb.append(','); 96 } else { 97 comma = true; 98 } 99 appendString(sb, String.valueOf(entry.getKey())); 100 sb.append(':'); 101 appendValue(sb, entry.getValue()); 102 } 103 return sb.append('}'); 104 } 105 106 private static StringBuilder appendList(StringBuilder sb, List<Object> list) { 107 sb.append('['); 108 boolean comma = false; 109 for (Object val : list) { 110 if (comma) { 111 sb.append(','); 112 } else { 113 comma = true; 114 } 115 appendValue(sb, val); 116 } 117 return sb.append(']'); 118 } 119 120 private static StringBuilder appendString(StringBuilder sb, String str) { 121 sb.append('"'); 122 for (int i = 0; i < str.length(); i++) { 123 char c = str.charAt(i); 124 switch (c) { 125 case '"': 126 sb.append("\\\""); 127 break; 128 case '\\': 129 sb.append("\\\\"); 130 break; 131 case '\b': 132 sb.append("\\b"); 133 break; 134 case '\f': 135 sb.append("\\f"); 136 break; 137 case '\n': 138 sb.append("\\n"); 139 break; 140 case '\r': 141 sb.append("\\r"); 142 break; 143 case '\t': 144 sb.append("\\t"); 145 break; 146 default: { 147 if (Character.isISOControl(c)) { 148 sb.append("\\u00"); 149 sb.append(Character.forDigit((c >> 4) & 0xF, 16)); 150 sb.append(Character.forDigit(c & 0xF, 16)); 151 } else { 152 sb.append(c); 153 } 154 } 155 } 156 } 157 return sb.append('"'); 158 } 159 160 private static StringBuilder appendByteArray(StringBuilder sb, byte[] arr) { 161 if (arr.length > 0) { 162 sb.append("0x"); 163 for (byte b : arr) { 164 sb.append(String.format("%02x", b)); 165 } 166 return sb; 167 } 168 return sb.append("null"); 169 } 170 }