001package org.hl7.fhir.utilities.json.model; 002 003import java.text.ParseException; 004import java.text.SimpleDateFormat; 005import java.time.Instant; 006import java.time.OffsetDateTime; 007import java.time.ZoneOffset; 008import java.util.ArrayList; 009import java.util.Date; 010import java.util.HashMap; 011import java.util.List; 012import java.util.Map; 013import java.util.Map.Entry; 014 015import org.hl7.fhir.utilities.Utilities; 016import org.hl7.fhir.utilities.json.JsonException; 017 018 019public class JsonObject extends JsonElement { 020 021 private List<JsonProperty> properties = new ArrayList<>(); 022 private Map<String, JsonProperty> propMap = new HashMap<>(); 023 private boolean extraComma; // json5 support 024 025 public JsonElementType type() { 026 return JsonElementType.OBJECT; 027 } 028 029 public JsonObject add(String name, JsonElement value) throws JsonException { 030 check(name != null, "Name is null"); 031 check(value != null, "Value is null"); 032 if (get(name) != null) { 033 check(false, "Name '"+name+"' already exists (value = "+get(name).toString()+")"); 034 } 035 JsonProperty p = new JsonProperty(name, value); 036 properties.add(p); 037 propMap.put(name, p); 038 return this; 039 } 040 041 // this is used by the parser which can allow duplicates = true (for the validator). You should not otherwise use it 042 public JsonObject addForParser(String name, JsonElement value, boolean noComma, boolean nameUnquoted, boolean valueUnquoted) throws JsonException { 043 check(name != null, "Name is null"); 044 check(value != null, "Value is null"); 045 JsonProperty p = new JsonProperty(name, value); 046 p.setNoComma(noComma); 047 p.setUnquotedName(nameUnquoted); 048 p.setUnquotedValue(valueUnquoted); 049 properties.add(p); 050 propMap.put(name, p); // last duplicate wins 051 return this; 052 } 053 054 public JsonObject add(String name, String value) throws JsonException { 055 check(name != null, "Name is null"); 056 return add(name, value == null ? new JsonNull() : new JsonString(value)); 057 } 058 059 public JsonObject add(String name, boolean value) throws JsonException { 060 check(name != null, "Name is null"); 061 return add(name, new JsonBoolean(value)); 062 } 063 064 public JsonObject add(String name, int value) throws JsonException { 065 check(name != null, "Name is null"); 066 return add(name, new JsonNumber(value)); 067 } 068 069 public JsonObject set(String name, JsonElement value) throws JsonException { 070 check(name != null, "Name is null"); 071 check(value != null, "Value is null"); 072 JsonProperty p = propMap.get(name); 073 if (p != null) { 074 p.setValue(value); 075 return this; 076 } else { 077 return add(name, value); 078 } 079 } 080 081 public JsonObject set(String name, Instant value) throws JsonException { 082 String v = value == null ? null : value.toString(); 083 return set(name, v); 084 } 085 086 087 public JsonObject set(String name, String value) throws JsonException { 088 check(name != null, "Name is null"); 089 JsonProperty p = propMap.get(name); 090 if (p != null) { 091 p.setValue(value == null ? new JsonNull() : new JsonString(value)); 092 return this; 093 } else { 094 return add(name, value == null ? new JsonNull() : new JsonString(value)); 095 } 096 } 097 098 public JsonObject set(String name, boolean value) throws JsonException { 099 check(name != null, "Name is null"); 100 JsonProperty p = propMap.get(name); 101 if (p != null) { 102 p.setValue(new JsonBoolean(value)); 103 return this; 104 } else { 105 return add(name, new JsonBoolean(value)); 106 } 107 } 108 109 public JsonObject set(String name, int value) throws JsonException { 110 check(name != null, "Name is null"); 111 JsonProperty p = propMap.get(name); 112 if (p != null) { 113 p.setValue(new JsonNumber(value)); 114 return this; 115 } else { 116 return add(name, new JsonNumber(value)); 117 } 118 } 119 120 public JsonElement get(String name) { 121 if (propMap.containsKey(name)) { 122 return propMap.get(name).getValue(); 123 } else { 124 return null; 125 } 126 } 127 128 public boolean has(String name) { 129 return propMap.containsKey(name); 130 } 131 132 public boolean has(String... names) { 133 for (String n : names) { 134 if (propMap.containsKey(n)) { 135 return true; 136 } 137 } 138 return false; 139 } 140 141 public void remove(String name) { 142 if (propMap.containsKey(name)) { 143 propMap.remove(name); 144 properties.removeIf((JsonProperty item) -> name.equals(item.getName())); 145 } 146 } 147 148 public List<JsonProperty> getProperties() { 149 return properties; 150 } 151 152 public List<String> getNames() { 153 return Utilities.sorted(propMap.keySet()); 154 } 155 156 public String str(String name) { 157 if (hasPrimitive(name)) { 158 return get(name).asJsonPrimitive().getValue(); 159 } else { 160 return null; 161 } 162 } 163 164 public boolean hasObject(String name) { 165 return propMap.containsKey(name) && propMap.get(name).getValue().type() == JsonElementType.OBJECT; 166 } 167 168 public boolean hasArray(String name) { 169 return propMap.containsKey(name) && propMap.get(name).getValue().type() == JsonElementType.ARRAY; 170 } 171 172 public boolean hasPrimitive(String name) { 173 return propMap.containsKey(name) && propMap.get(name).getValue() instanceof JsonPrimitive; 174 } 175 176 public boolean hasString(String name) { 177 return propMap.containsKey(name) && propMap.get(name).getValue().type() == JsonElementType.STRING; 178 } 179 180 public boolean hasNumber(String name) { 181 return propMap.containsKey(name) && propMap.get(name).getValue().type() == JsonElementType.NUMBER; 182 } 183 184 public boolean hasBoolean(String name) { 185 return propMap.containsKey(name) && propMap.get(name).getValue().type() == JsonElementType.BOOLEAN; 186 } 187 188 public boolean hasNull(String name) { 189 return propMap.containsKey(name) && propMap.get(name).getValue().type() == JsonElementType.NULL; 190 } 191 192 193 public JsonObject getJsonObject(String name) { 194 return hasObject(name) ? (JsonObject) get(name) : null; 195 } 196 197 public JsonString getJsonString(String name) { 198 return hasString(name) ? (JsonString) get(name) : null; 199 } 200 201 public JsonBoolean getJsonBoolean(String name) { 202 return hasBoolean(name) ? (JsonBoolean) get(name) : null; 203 } 204 205 public JsonNumber getJsonNumber(String name) { 206 return hasNumber(name) ? (JsonNumber) get(name) : null; 207 } 208 209 public JsonNull getJsonNull(String name) { 210 return hasNull(name) ?(JsonNull) get(name) : null; 211 } 212 213 public JsonArray getJsonArray(String name) { 214 return hasArray(name) ? (JsonArray) get(name) : null; 215 } 216 217 public Integer asInteger(String name) { 218 if (hasNumber(name)) { 219 return ((JsonNumber) get(name)).getInteger(); 220 } 221 if (hasPrimitive(name)) { 222 String s = asString(name); 223 if (Utilities.isInteger(s)) { 224 return Integer.parseInt(s); 225 } 226 } 227 return null; 228 } 229 230 public String asString(String name) { 231 return hasPrimitive(name) ? ((JsonPrimitive) get(name)).getValue() : null; 232 } 233 234 public String asString(String... names) { 235 for (String n : names) { 236 if (hasPrimitive(n)) { 237 return asString(n); 238 } 239 } 240 return null; 241 } 242 243 public boolean asBoolean(String name) { 244 if (hasBoolean(name)) { 245 return ((JsonBoolean) get(name)).isValue(); 246 } 247 if (hasPrimitive(name)) { 248 String s = asString(name); 249 if ("true".equals(s)) { 250 return true; 251 } 252 if ("false".equals(s)) { 253 return false; 254 } 255 } 256 return false; 257 } 258 259 public Instant asDate(String name) { 260 String source = asString(name); 261 if (Utilities.noString(source)) { 262 return null; 263 } else { 264 OffsetDateTime odt = OffsetDateTime.parse(source); 265 return odt.toInstant(); 266 } 267 } 268 269 public Instant asInstant(String name) throws ParseException { 270 String source = asString(name); 271 if (Utilities.noString(source) || "null".equals(source)) { 272 return null; 273 } else if (source.length() <= 10) { 274 Date d = new SimpleDateFormat("yyyy-mm-dd").parse(source); 275 return d.toInstant(); 276 } else { 277 OffsetDateTime odt = OffsetDateTime.parse(source); 278 return odt.toInstant(); 279 } 280 } 281 282 public JsonObject forceObject(String name) throws JsonException { 283 if (has(name) && !hasObject(name)) { 284 remove(name); 285 } 286 if (!has(name)) { 287 add(name, new JsonObject()); 288 } 289 return getJsonObject(name); 290 } 291 292 public JsonArray forceArray(String name) throws JsonException { 293 if (has(name) && !hasArray(name)) { 294 remove(name); 295 } 296 if (!has(name)) { 297 add(name, new JsonArray()); 298 } 299 return getJsonArray(name); 300 } 301 302 public List<JsonObject> getJsonObjects(String name) { 303 List<JsonObject> res = new ArrayList<>(); 304 if (hasArray(name)) { 305 res.addAll(getJsonArray(name).asJsonObjects()); 306 } else if (hasObject(name)) { 307 res.add(getJsonObject(name)); 308 } 309 return res; 310 } 311 312 public List<String> getStrings(String name) { 313 List<String> res = new ArrayList<>(); 314 if (hasArray(name)) { 315 res.addAll(getJsonArray(name).asStrings()); 316 } else if (hasPrimitive(name)) { 317 res.add(asString(name)); 318 } 319 return res; 320 } 321 322 public JsonObject deepCopy() { 323 return (JsonObject) make().copy(this); 324 } 325 326 @Override 327 protected JsonElement copy(JsonElement other) { 328 JsonObject o = (JsonObject) other; 329 for (JsonProperty p : o.getProperties()) { 330 add(p.getName(), p.getValue().deepCopy()); 331 } 332 return this; 333 } 334 335 @Override 336 protected JsonElement make() { 337 return new JsonObject(); 338 } 339 340 public JsonObject findByStringProp(String arrName, String prop, String value) { 341 for (JsonObject obj : getJsonObjects(arrName)) { 342 if (obj.has(prop) && value.equals(obj.asString(prop))) 343 return obj; 344 } 345 return null; 346 } 347 348 public void merge(JsonObject source) { 349 for (JsonProperty pp : source.getProperties()) { 350 if (has(pp.getName())) { 351 JsonElement te = get(pp.getName()); 352 if (te.isJsonObject() && pp.getValue().isJsonObject()) { 353 ((JsonObject) te).merge((JsonObject) pp.getValue()); 354 } else { 355 set(pp.getName(), pp.getValue()); 356 } 357 } else { 358 add(pp.getName(), pp.getValue()); 359 } 360 } 361 } 362 363 364 @Override 365 public String toString() { 366 StringBuilder b = new StringBuilder(); 367 b.append("{ "); 368 boolean first = true; 369 for (JsonProperty p : properties) { 370 if (first) first = false; else b.append(", "); 371 b.append(p.toString()); 372 } 373 b.append(" }"); 374 return b.toString(); 375 } 376 377 public boolean isExtraComma() { 378 return extraComma; 379 } 380 381 public void setExtraComma(boolean extraComma) { 382 this.extraComma = extraComma; 383 } 384 385 386}