001package org.hl7.fhir.r5.elementmodel; 002 003import java.io.ByteArrayInputStream; 004 005/* 006 Copyright (c) 2011+, HL7, Inc. 007 All rights reserved. 008 009 Redistribution and use in source and binary forms, with or without modification, 010 are permitted provided that the following conditions are met: 011 012 * Redistributions of source code must retain the above copyright notice, this 013 list of conditions and the following disclaimer. 014 * Redistributions in binary form must reproduce the above copyright notice, 015 this list of conditions and the following disclaimer in the documentation 016 and/or other materials provided with the distribution. 017 * Neither the name of HL7 nor the names of its contributors may be used to 018 endorse or promote products derived from this software without specific 019 prior written permission. 020 021 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 022 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 023 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 024 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 025 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 026 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 027 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 028 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 029 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 030 POSSIBILITY OF SUCH DAMAGE. 031 032 */ 033 034 035 036import java.io.IOException; 037import java.io.InputStream; 038import java.io.OutputStream; 039import java.io.OutputStreamWriter; 040import java.math.BigDecimal; 041import java.nio.charset.StandardCharsets; 042import java.util.ArrayList; 043import java.util.HashMap; 044import java.util.HashSet; 045import java.util.List; 046import java.util.Map; 047import java.util.Map.Entry; 048import java.util.Set; 049 050import org.hl7.fhir.exceptions.FHIRException; 051import org.hl7.fhir.exceptions.FHIRFormatError; 052import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; 053import org.hl7.fhir.r5.context.ContextUtilities; 054import org.hl7.fhir.r5.context.IWorkerContext; 055import org.hl7.fhir.r5.elementmodel.Element.SpecialElement; 056import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat; 057import org.hl7.fhir.r5.fhirpath.FHIRPathEngine; 058import org.hl7.fhir.r5.formats.IParser.OutputStyle; 059import org.hl7.fhir.r5.formats.JsonCreator; 060import org.hl7.fhir.r5.formats.JsonCreatorCanonical; 061import org.hl7.fhir.r5.formats.JsonCreatorDirect; 062import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; 063import org.hl7.fhir.r5.model.ElementDefinition; 064import org.hl7.fhir.r5.model.StructureDefinition; 065import org.hl7.fhir.r5.utils.ToolingExtensions; 066import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; 067import org.hl7.fhir.utilities.StringPair; 068import org.hl7.fhir.utilities.TextFile; 069import org.hl7.fhir.utilities.Utilities; 070import org.hl7.fhir.utilities.VersionUtilities; 071import org.hl7.fhir.utilities.i18n.I18nConstants; 072import org.hl7.fhir.utilities.json.model.JsonArray; 073import org.hl7.fhir.utilities.json.model.JsonComment; 074import org.hl7.fhir.utilities.json.model.JsonElement; 075import org.hl7.fhir.utilities.json.model.JsonNull; 076import org.hl7.fhir.utilities.json.model.JsonObject; 077import org.hl7.fhir.utilities.json.model.JsonPrimitive; 078import org.hl7.fhir.utilities.json.model.JsonProperty; 079import org.hl7.fhir.utilities.validation.ValidationMessage; 080import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 081import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; 082import org.hl7.fhir.utilities.xhtml.XhtmlParser; 083 084 085public class JsonParser extends ParserBase { 086 087 private JsonCreator json; 088 private boolean allowComments; 089 private boolean elideElements; 090// private boolean suppressResourceType; 091 092 private Element baseElement; 093 private boolean markedXhtml; 094 095 public JsonParser(IWorkerContext context, ProfileUtilities utilities) { 096 super(context, utilities); 097 098 } 099 100 public JsonParser(IWorkerContext context) { 101 super(context); 102 } 103 104 public Element parse(String source, String type) throws Exception { 105 return parse(source, type, false); 106 } 107 108 public Element parse(String source, String type, boolean inner) throws Exception { 109 ValidatedFragment focusFragment = new ValidatedFragment(ValidatedFragment.FOCUS_NAME, "json", source.getBytes(StandardCharsets.UTF_8), false); 110 JsonObject obj = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(source, true, true); 111 String path = "/"+type; 112 StructureDefinition sd = getDefinition(focusFragment.getErrors(), -1, -1, type); 113 if (sd == null) { 114 return null; 115 } 116 117 if (inner) { 118 // we have an anonymous wrapper that has an arbitrarily named property with the specified type. We're going to invent a snapshot for that 119 sd = new StructureDefinition(); 120 sd.setType("Wrapper"); 121 ElementDefinition bEd = sd.getSnapshot().addElement(); 122 ElementDefinition nEd = sd.getSnapshot().addElement(); 123 bEd.setPath("Wrapper"); 124 nEd.setPath("Wrapper."+obj.getProperties().get(0).getName()); 125 nEd.addType().setCode(type); 126 nEd.setMax(obj.getProperties().get(0).getValue().isJsonArray() ? "*" : "1"); 127 } 128 Element result = new Element(type, new Property(context, sd.getSnapshot().getElement().get(0), sd, this.getProfileUtilities(), this.getContextUtilities())).setFormat(FhirFormat.JSON); 129 result.setPath(type); 130 checkObject(focusFragment.getErrors(), obj, result, path); 131 result.setType(type); 132 parseChildren(focusFragment.getErrors(), path, obj, result, true, null); 133 result.numberChildren(); 134 return result; 135 } 136 137 138 @Override 139 public List<ValidatedFragment> parse(InputStream inStream) throws IOException, FHIRException { 140 return parse(inStream, 0); 141 } 142 143 public List<ValidatedFragment> parse(InputStream inStream, int line) throws IOException, FHIRException { 144// long start = System.currentTimeMillis(); 145 byte[] content = TextFile.streamToBytes(inStream); 146 ValidatedFragment focusFragment = new ValidatedFragment(ValidatedFragment.FOCUS_NAME, "json", content, false); 147 148 ByteArrayInputStream stream = new ByteArrayInputStream(content); 149 150 // if we're parsing at this point, then we're going to use the custom parser 151 String source = TextFile.streamToString(stream); 152 JsonObject obj = null; 153 154 if (policy == ValidationPolicy.EVERYTHING) { 155 try { 156 obj = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(source, true, true, line); 157 } catch (Exception e) { 158 logError(focusFragment.getErrors(), ValidationMessage.NO_RULE_DATE, -1, -1, null, IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_JSON_, e.getMessage()), IssueSeverity.FATAL); 159 } 160 } else { 161 obj = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(source, true, true, line); 162 } 163 164 if (obj != null) { 165 focusFragment.setElement(parse(focusFragment.getErrors(), obj)); 166 } 167 List<ValidatedFragment> res = new ArrayList<>(); 168 res.add(focusFragment); 169 170// long t =System.currentTimeMillis()-start; 171// System.out.println("json parser: "+(t)+"ms, "+(content.length/1024)+"kb "+(t == 0 ? "" : " @ "+(content.length / t)+"kb/s")); 172 return res; 173 } 174 175 public Element parse(List<ValidationMessage> errors, JsonObject object) throws FHIRException { 176 return parse(errors, object, null); 177 } 178 179 public Element parse(List<ValidationMessage> errors, JsonObject object, String statedPath) throws FHIRException { 180 if (object == null) { 181 System.out.println("What?"); 182 } 183 StructureDefinition sd = getLogical(); 184 String name; 185 String path; 186 if (sd == null) { 187 JsonElement rt = object.get("resourceType"); 188 if (rt == null) { 189 logError(errors, ValidationMessage.NO_RULE_DATE, line(object), col(object), "$", IssueType.INVALID, context.formatMessage(I18nConstants.UNABLE_TO_FIND_RESOURCETYPE_PROPERTY), IssueSeverity.FATAL); 190 return null; 191 } else if (!rt.isJsonString()) { 192 logError(errors, "2022-11-26", line(object), col(object), "$", IssueType.INVALID, context.formatMessage(I18nConstants.RESOURCETYPE_PROPERTY_WRONG_TYPE, rt.type().toName()), IssueSeverity.FATAL); 193 return null; 194 } else { 195 name = rt.asString(); 196 197 sd = getDefinition(errors, line(object), col(object), name); 198 if (sd == null) { 199 return null; 200 } 201 } 202 path = statedPath == null ? name : statedPath; 203 } else { 204 name = sd.getType(); 205 path = statedPath == null ? sd.getTypeTail() : statedPath; 206 } 207 baseElement = new Element(name, new Property(context, sd.getSnapshot().getElement().get(0), sd, this.getProfileUtilities(), this.getContextUtilities())).setFormat(FhirFormat.JSON); 208 checkObject(errors, object, baseElement, path); 209 baseElement.markLocation(line(object), col(object)); 210 baseElement.setType(name); 211 baseElement.setPath(statedPath == null ? baseElement.fhirTypeRoot() : statedPath); 212 parseChildren(errors, path, object, baseElement, true, null); 213 baseElement.numberChildren(); 214 return baseElement; 215 } 216 217 private void checkObject(List<ValidationMessage> errors, JsonObject object, Element b, String path) { 218 b.setNativeObject(object); 219 checkComments(errors, object, b, path); 220 if (policy == ValidationPolicy.EVERYTHING) { 221 if (object.getProperties().size() == 0) { 222 logError(errors, ValidationMessage.NO_RULE_DATE, line(object), col(object), path, IssueType.INVALID, context.formatMessage(I18nConstants.OBJECT_MUST_HAVE_SOME_CONTENT), IssueSeverity.ERROR); 223 } 224 } 225 } 226 227 private void checkComments(List<ValidationMessage> errors, JsonElement element, Element b, String path) throws FHIRFormatError { 228 if (element != null && element.hasComments()) { 229 if (allowComments) { 230 for (JsonComment c : element.getComments()) { 231 b.getComments().add(c.getContent()); 232 } 233 } else { 234 for (JsonComment c : element.getComments()) { 235 logError(errors, "2022-11-26", c.getStart().getLine(), c.getStart().getCol(), path, IssueType.INVALID, context.formatMessage(I18nConstants.JSON_COMMENTS_NOT_ALLOWED), IssueSeverity.ERROR); 236 } 237 } 238 } 239 } 240 241 private List<Property> parseChildren(List<ValidationMessage> errors, String path, JsonObject object, Element element, boolean hasResourceType, List<Property> properties) throws FHIRException { 242 if (properties == null) { 243 // save time refetching these if we're in a loop 244 properties = element.getProperty().getChildProperties(element.getName(), null); 245 } 246 processChildren(errors, path, object); 247 248 // first pass: process the properties 249 for (Property property : properties) { 250 parseChildItem(errors, path, object.getProperties(), element, property); 251 } 252 253 // second pass: check for things not processed (including duplicates) 254 checkNotProcessed(errors, path, element, hasResourceType, object.getProperties()); 255 256 257 if (object.isExtraComma()) { 258 logError(errors, "2022-11-26", object.getEnd().getLine(), object.getEnd().getCol(), path, IssueType.INVALID, context.formatMessage(I18nConstants.JSON_COMMA_EXTRA, "Object"), IssueSeverity.ERROR); 259 } 260 return properties; 261 } 262 263 private void checkNotProcessed(List<ValidationMessage> errors, String path, Element element, boolean hasResourceType, List<JsonProperty> children) { 264 if (policy != ValidationPolicy.NONE) { 265 for (JsonProperty e : children) { 266 if (e.getTag() == 0) { 267 StructureDefinition sd = element.getProperty().isLogical() ? getContextUtilities().fetchByJsonName(e.getName()) : null; 268 if (sd != null) { 269 Property property = new Property(context, sd.getSnapshot().getElementFirstRep(), sd, element.getProperty().getUtils(), element.getProperty().getContextUtils()); 270 parseChildItem(errors, path, children, element, property); 271 } else if ("fhir_comments".equals(e.getName()) && (VersionUtilities.isR2BVer(context.getVersion()) || VersionUtilities.isR2Ver(context.getVersion()))) { 272 if (!e.getValue().isJsonArray()) { 273 logError(errors, "2022-12-17", line(e.getValue()), col(e.getValue()), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.ILLEGAL_COMMENT_TYPE, e.getValue().type().toName()), IssueSeverity.ERROR); 274 } else { 275 for (JsonElement c : e.getValue().asJsonArray()) { 276 if (!c.isJsonString()) { 277 logError(errors, "2022-12-17", line(e.getValue()), col(e.getValue()), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.ILLEGAL_COMMENT_TYPE, c.type().toName()), IssueSeverity.ERROR); 278 } else { 279 element.getComments().add(c.asString()); 280 } 281 } 282 } 283 } else if (hasResourceType && "resourceType".equals(e.getName())) { 284 // nothing 285 } else { 286 JsonProperty p = getFoundJsonPropertyByName(e.getName(), children); 287 if (p != null) { 288 logError(errors, "2022-11-26", line(e.getValue()), col(e.getValue()), path, IssueType.INVALID, context.formatMessage(I18nConstants.DUPLICATE_JSON_PROPERTY_KEY, e.getName()), IssueSeverity.ERROR); 289 } else { 290 logError(errors, ValidationMessage.NO_RULE_DATE, line(e.getValue()), col(e.getValue()), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_, e.getName()), IssueSeverity.ERROR); 291 } 292 } 293 } 294 } 295 } 296 } 297 298 private JsonProperty getFoundJsonPropertyByName(String name, List<JsonProperty> children) { 299 int hash = name.hashCode(); 300 for (JsonProperty p : children) { 301 if (p.getTag() == 1 && hash == p.getNameHash()) { 302 return p; 303 } 304 } 305 return null; 306 } 307 308 private JsonProperty getJsonPropertyByName(String name, List<JsonProperty> children) { 309 int hash = name.hashCode(); 310 for (JsonProperty p : children) { 311 if (p.getTag() == 0 && hash == p.getNameHash()) { 312 return p; 313 } 314 } 315 return null; 316 } 317 318 private JsonProperty getJsonPropertyByBaseName(String name, List<JsonProperty> children) { 319 for (JsonProperty p : children) { 320 if (p.getTag() == 0 && p.getName().startsWith(name)) { 321 return p; 322 } 323 } 324 return null; 325 } 326 327 private void processChildren(List<ValidationMessage> errors, String path, JsonObject object) { 328 for (JsonProperty p : object.getProperties()) { 329 if (p.isUnquotedName()) { 330 logError(errors, "2022-11-26", line(p.getValue()), col(p.getValue()), path, IssueType.INVALID, context.formatMessage(I18nConstants.JSON_PROPERTY_NO_QUOTES, p.getName()), IssueSeverity.ERROR); 331 } 332 if (p.isNoComma()) { 333 logError(errors, "2022-11-26", line(p.getValue()), col(p.getValue()), path, IssueType.INVALID, context.formatMessage(I18nConstants.JSON_COMMA_MISSING), IssueSeverity.ERROR); 334 } 335 } 336 } 337 338 public void parseChildItem(List<ValidationMessage> errors, String path, List<JsonProperty> children, Element context, Property property) { 339 JsonProperty jp = getJsonPropertyByName(property.getJsonName(), children); 340 if (property.isChoice() || property.getDefinition().getPath().endsWith("data[x]")) { 341 if (property.isJsonPrimitiveChoice()) { 342 if (jp != null) { 343 jp.setTag(1); 344 JsonElement je = jp.getValue(); 345 String type = getTypeFromJsonType(je); 346 if (type == null) { 347 logError(errors, ValidationMessage.NO_RULE_DATE, line(je), col(je), path, IssueType.STRUCTURE, this.context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_TYPE, describeType(je), property.getName(), property.typeSummary()), IssueSeverity.ERROR); 348 } else if (property.hasType(type)) { 349 Property np = new Property(property.getContext(), property.getDefinition(), property.getStructure(), property.getUtils(), property.getContextUtils(), type); 350 parseChildPrimitive(errors, jp, getJsonPropertyByName("_"+property.getJsonName(), children), context, np, path, property.getName(), false); 351 } else { 352 logError(errors, ValidationMessage.NO_RULE_DATE, line(je), col(je), path, IssueType.STRUCTURE, this.context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_TYPE_WRONG, describeType(je), property.getName(), type, property.typeSummary()), IssueSeverity.ERROR); 353 } 354 } 355 } else { 356 String baseName = property.getJsonName().substring(0, property.getName().length()-3); 357 jp = getJsonPropertyByBaseName(baseName, children); 358 JsonProperty jp1 = getJsonPropertyByBaseName("_"+baseName, children); 359 if (jp != null || jp1 != null) { 360 for (TypeRefComponent type : property.getDefinition().getType()) { 361 String eName = baseName + Utilities.capitalize(type.getWorkingCode()); 362 if ((jp != null && jp.getName().equals(eName) || (jp1 != null && jp1.getName().equals("_"+eName)))) { 363 if (!isPrimitive(type.getWorkingCode()) && jp != null) { 364 parseChildComplex(errors, path, jp, context, property, eName, false); 365 break; 366 } else if (isPrimitive(type.getWorkingCode()) && (jp != null || jp1 != null)) { 367 parseChildPrimitive(errors, jp, jp1, context, property, path, eName, false); 368 break; 369 } 370 } 371 } 372 } 373 } 374 } else if (property.isPrimitive(property.getType(null))) { 375 parseChildPrimitive(errors, jp, getJsonPropertyByName("_"+property.getJsonName(), children), context, property, path, property.getJsonName(), property.hasJsonName()); 376 } else if (jp != null) { 377 parseChildComplex(errors, path, jp, context, property, property.getJsonName(), property.hasJsonName()); 378 } 379 } 380 381 382 private String getTypeFromJsonType(JsonElement je) { 383 if (je.isJsonPrimitive()) { 384 JsonPrimitive p = je.asJsonPrimitive(); 385 if (p.isJsonString()) { 386 return "string"; 387 } else if (p.isJsonBoolean()) { 388 return "boolean"; 389 } else { 390 String s = p.asString(); 391 if (Utilities.isInteger(s)) { 392 return "integer"; 393 } else { 394 return "decimal"; 395 } 396 } 397 } else { 398 return null; 399 } 400 } 401 402 private void parseChildComplex(List<ValidationMessage> errors, String path, JsonProperty p, Element element, Property property, String name, boolean isJsonName) throws FHIRException { 403 String npath = path+"."+property.getName(); 404 String fpath = element.getPath()+"."+property.getName(); 405 if (p != null) { p.setTag(1); } 406 JsonElement e = p == null ? null : p.getValue(); 407 if (property.isList() && !property.isJsonKeyArray() && (e instanceof JsonArray)) { 408 JsonArray arr = (JsonArray) e; 409 if (arr.isExtraComma()) { 410 logError(errors, "2022-11-26", arr.getEnd().getLine(), arr.getEnd().getCol(), npath, IssueType.INVALID, context.formatMessage(I18nConstants.JSON_COMMA_EXTRA, "Array"), IssueSeverity.ERROR); 411 } 412 if (arr.size() == 0) { 413 if (property.canBeEmpty()) { 414 // nothing 415 } else { 416 logError(errors, ValidationMessage.NO_RULE_DATE, line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ARRAY_CANNOT_BE_EMPTY), IssueSeverity.ERROR); 417 } 418 } 419 int c = 0; 420 List<Property> properties = null; 421 for (JsonElement am : arr) { 422 properties = parseChildComplexInstance(errors, npath+"["+c+"]", fpath+"["+c+"]", element, property, name, am, c == 0 ? arr : null, path, properties); 423 c++; 424 } 425 } else if (property.isJsonKeyArray()) { 426 String code = property.getJsonKeyProperty(); 427 List<Property> properties = property.getChildProperties(element.getName(), null); 428 if (properties.size() != 2) { 429 logError(errors, ValidationMessage.NO_RULE_DATE, line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.OBJECT_CANNOT_BE_KEYED_ARRAY_CHILD_COUNT, propNames(properties)), IssueSeverity.ERROR); 430 } else { 431 Property propK = properties.get(0); 432 Property propV = properties.get(1); 433 if (!propK.getName().equals(code)) { 434 logError(errors, ValidationMessage.NO_RULE_DATE, line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.OBJECT_CANNOT_BE_KEYED_ARRAY_PROP_NAME, propNames(properties)), IssueSeverity.ERROR); 435 } else if (!propK.isPrimitive()) { 436 logError(errors, ValidationMessage.NO_RULE_DATE, line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.OBJECT_CANNOT_BE_KEYED_ARRAY_PROP_TYPE, propNames(properties), propK.typeSummary()), IssueSeverity.ERROR); 437 } else if (propV.isList()) { 438 logError(errors, ValidationMessage.NO_RULE_DATE, line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.OBJECT_CANNOT_BE_KEYED_ARRAY_NO_LIST, propV.getName()), IssueSeverity.ERROR); 439 } else if (propV.isChoice() && propV.getName().endsWith("[x]")) { 440 logError(errors, ValidationMessage.NO_RULE_DATE, line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.OBJECT_CANNOT_BE_KEYED_ARRAY_NO_CHOICE, propV.getName()), IssueSeverity.ERROR); 441 } else if (!(e instanceof JsonObject)) { 442 logError(errors, ValidationMessage.NO_RULE_DATE, line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_AN_OBJECT_NOT_, describe(e)), IssueSeverity.ERROR); 443 } else { 444 JsonObject o = (JsonObject) e; 445 if (o.isExtraComma()) { 446 logError(errors, "2022-11-26", o.getEnd().getLine(), o.getEnd().getCol(), npath, IssueType.INVALID, context.formatMessage(I18nConstants.JSON_COMMA_EXTRA, "Object"), IssueSeverity.ERROR); 447 } 448 449 int i = 0; 450 Set<String> names = new HashSet<>(); 451 for (JsonProperty pv : o.getProperties()) { 452 if (names.contains(pv.getName())) { 453 logError(errors, "2022-11-26", line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.DUPLICATE_JSON_PROPERTY_KEY, pv.getName()), IssueSeverity.ERROR); 454 } else { 455 names.add(pv.getName()); 456 pv.setTag(1); 457 } 458 // create an array entry 459 String npathArr = path+"."+property.getName()+"["+i+"]"; 460 String fpathArr = element.getPath()+"."+property.getName()+"["+i+"]"; 461 462 Element n = new Element(name, property).markLocation(line(pv.getValue()), col(pv.getValue())).setFormat(FhirFormat.JSON); 463 n.setPath(fpath); 464 element.getChildren().add(n); 465 // handle the key 466 String fpathKey = fpathArr+"."+propK.getName(); 467 Element nKey = new Element(code, propK).markLocation(line(pv.getValue()), col(pv.getValue())).setFormat(FhirFormat.JSON); 468 checkComments(errors, pv.getValue(), n, fpathArr); 469 nKey.setPath(fpathKey); 470 n.getChildren().add(nKey); 471 nKey.setValue(pv.getName()); 472 473 474 boolean ok = true; 475 Property pvl = propV; 476 if (propV.isJsonPrimitiveChoice()) { 477 ok = false; 478 String type = getTypeFromJsonType(pv.getValue()); 479 if (type == null) { 480 logError(errors, ValidationMessage.NO_RULE_DATE, line(pv.getValue()), col(pv.getValue()), path, IssueType.STRUCTURE, this.context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_TYPE, describeType(pv.getValue()), propV.getName(), propV.typeSummary()), IssueSeverity.ERROR); 481 } else if (propV.hasType(type)) { 482 pvl = new Property(propV.getContext(), propV.getDefinition(), propV.getStructure(), propV.getUtils(), propV.getContextUtils(), type); 483 ok = true; 484 } else if (propV.getDefinition().getType().size() == 1 && propV.typeIsConsistent(type)) { 485 pvl = new Property(propV.getContext(), propV.getDefinition(), propV.getStructure(), propV.getUtils(), propV.getContextUtils(), propV.getType()); 486 } else { 487 logError(errors, ValidationMessage.NO_RULE_DATE, line(pv.getValue()), col(pv.getValue()), path, IssueType.STRUCTURE, this.context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_TYPE_WRONG, describeType(pv.getValue()), propV.getName(), type, propV.typeSummary()), IssueSeverity.ERROR); 488 } 489 } 490 if (ok) { 491 // handle the value 492 String npathV = npathArr+"."+pvl.getName(); 493 String fpathV = fpathArr+"."+pvl.getName(); 494 if (propV.isPrimitive(pvl.getType(null))) { 495 parseChildPrimitiveInstance(errors, n, pvl, pvl.getName(), false, npathV, fpathV, pv.getValue(), null); 496 } else if (pv.getValue() instanceof JsonObject || pv.getValue() instanceof JsonNull) { 497 parseChildComplexInstance(errors, npathV, fpathV, n, pvl, pvl.getName(), pv.getValue(), null, null, null); 498 } else { 499 logError(errors, ValidationMessage.NO_RULE_DATE, line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_AN_OBJECT_NOT_, describe(pv.getValue())), IssueSeverity.ERROR); 500 } 501 } 502 i++; 503 } 504 } 505 } 506 } else { 507 if (property.isList()) { 508 logError(errors, ValidationMessage.NO_RULE_DATE, line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_AN_ARRAY_NOT_, describe(e), name, path), IssueSeverity.ERROR); 509 } 510 parseChildComplexInstance(errors, npath, fpath, element, property, name, e, null, null, null); 511 } 512 } 513 514 private Object propNames(List<Property> properties) { 515 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); 516 for (Property p: properties) { 517 b.append(p.getName()); 518 } 519 return b.toString(); 520 } 521 522 private List<Property> parseChildComplexInstance(List<ValidationMessage> errors, String npath, String fpath, Element element, Property property, String name, JsonElement e, JsonElement commentContext, String commentPath, List<Property> properties) throws FHIRException { 523 if (property.hasTypeSpecifier()) { 524 FHIRPathEngine fpe = new FHIRPathEngine(context); 525 String type = null; 526 String cond = null; 527 for (StringPair sp : property.getTypeSpecifiers()) { 528 if (fpe.evaluateToBoolean(null, baseElement, baseElement, element, fpe.parse(sp.getName()))) { 529 type = sp.getValue(); 530 cond = sp.getName(); 531 break; 532 } 533 } 534 if (type != null) { 535 StructureDefinition sd = context.fetchResource(StructureDefinition.class, type); 536 if (sd == null) { 537 logError(errors, ValidationMessage.NO_RULE_DATE, line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.TYPE_SPECIFIER_ILLEGAL_TYPE, type, cond), IssueSeverity.ERROR); 538 } else { 539 if (sd.getAbstract()) { 540 logError(errors, ValidationMessage.NO_RULE_DATE, line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.TYPE_SPECIFIER_ABSTRACT_TYPE, type, cond), IssueSeverity.ERROR); 541 } 542 property = property.cloneToType(sd); 543 } 544 } else { 545 StructureDefinition sd = context.fetchTypeDefinition(property.getType()); 546 if (sd == null) { 547 logError(errors, ValidationMessage.NO_RULE_DATE, line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.TYPE_SPECIFIER_NM_ILLEGAL_TYPE, property.getType()), IssueSeverity.ERROR); 548 } else if (sd.getAbstract()) { 549 logError(errors, ValidationMessage.NO_RULE_DATE, line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.TYPE_SPECIFIER_NM_ABSTRACT_TYPE, property.getType()), IssueSeverity.ERROR); 550 } 551 } 552 } 553 if (e instanceof JsonObject) { 554 JsonObject child = (JsonObject) e; 555 Element n = new Element(name, property).markLocation(line(child), col(child)).setFormat(FhirFormat.JSON); 556 n.setPath(fpath); 557 checkComments(errors, commentContext, n, commentPath); 558 checkObject(errors, child, n, npath); 559 element.getChildren().add(n); 560 if (property.isResource()) { 561 parseResource(errors, npath, child, n, property); 562 } else { 563 return parseChildren(errors, npath, child, n, false, properties); 564 } 565 } else if (property.isNullable() && e instanceof JsonNull) { 566 // we create an element marked as a null element so we know something was present 567 JsonNull child = (JsonNull) e; 568 Element n = new Element(name, property).markLocation(line(child), col(child)).setFormat(FhirFormat.JSON); 569 checkComments(errors, commentContext, n, commentPath); 570 checkComments(errors, child, n, fpath); 571 n.setPath(fpath); 572 element.getChildren().add(n); 573 n.setNull(true); 574 // nothing to do, it's ok, but we treat it like it doesn't exist 575 } else { 576 logError(errors, ValidationMessage.NO_RULE_DATE, line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE__NOT_, (property.isList() ? "an Array" : "an Object"), describe(e), name, npath), IssueSeverity.ERROR); 577 } 578 return null; 579 } 580 581 private String describe(JsonElement e) { 582 if (e instanceof JsonArray) { 583 return "an Array"; 584 } 585 if (e instanceof JsonObject) { 586 return "an Object"; 587 } 588 if (e instanceof JsonNull) { 589 return "a Null"; 590 } 591 if (e instanceof JsonPrimitive) { 592 return "a Primitive property"; 593 } 594 return null; 595 } 596 597 private String describeType(JsonElement e) { 598 return e.type().toName(); 599 } 600 601// JsonProperty main = children.containsKey(name) ? children.get(name) : null; 602// JsonProperty fork = children.containsKey("_"+name) ? children.get("_"+name) : null; 603 private void parseChildPrimitive(List<ValidationMessage> errors, JsonProperty main, JsonProperty fork, Element element, Property property, String path, String name, boolean isJsonName) throws FHIRException { 604 String npath = path+"."+property.getName(); 605 String fpath = element.getPath()+"."+property.getName(); 606 if (main != null) { main.setTag(1); } 607 if (fork != null) { fork.setTag(1); } 608 609 if (main != null && main.getValue().isJsonString() && main.isUnquotedValue()) { 610 logError(errors, "2022-11-26", line(main.getValue()), col(main.getValue()), path, IssueType.INVALID, context.formatMessage(I18nConstants.JSON_PROPERTY_VALUE_NO_QUOTES, main.getName(), main.getValue().asString()), IssueSeverity.ERROR); 611 } 612 if (main != null || fork != null) { 613 if (property.isList()) { 614 boolean ok = true; 615 if (!(main == null || main.getValue() instanceof JsonArray)) { 616 logError(errors, ValidationMessage.NO_RULE_DATE, line(main.getValue()), col(main.getValue()), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_AN_ARRAY_NOT_, describe(main.getValue()), name, path), IssueSeverity.ERROR); 617 ok = false; 618 } 619 if (!(fork == null || fork.getValue() instanceof JsonArray)) { 620 logError(errors, ValidationMessage.NO_RULE_DATE, line(fork.getValue()), col(fork.getValue()), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_BASE_PROPERTY_MUST_BE_AN_ARRAY_NOT_, describe(main.getValue()), name, path), IssueSeverity.ERROR); 621 ok = false; 622 } 623 if (ok) { 624 JsonArray arr1 = (JsonArray) (main == null ? null : main.getValue()); 625 JsonArray arr2 = (JsonArray) (fork == null ? null : fork.getValue()); 626 if (arr1 != null && arr1.isExtraComma()) { 627 logError(errors, "2022-11-26", arr1.getEnd().getLine(), arr1.getEnd().getCol(), npath, IssueType.INVALID, context.formatMessage(I18nConstants.JSON_COMMA_EXTRA, "Array"), IssueSeverity.ERROR); 628 } 629 if (arr2 != null && arr2.isExtraComma()) { 630 logError(errors, "2022-11-26", arr2.getEnd().getLine(), arr2.getEnd().getCol(), npath, IssueType.INVALID, context.formatMessage(I18nConstants.JSON_COMMA_EXTRA, "Array"), IssueSeverity.ERROR); 631 } 632 633 for (int i = 0; i < Math.max(arrC(arr1), arrC(arr2)); i++) { 634 JsonElement m = arrI(arr1, i); 635 JsonElement f = arrI(arr2, i); 636 if (m != null && m.isJsonString() && arr1.isUnquoted(i)) { 637 logError(errors, "2022-11-26", line(m), col(m), path+"."+name+"["+i+"]", IssueType.INVALID, context.formatMessage(I18nConstants.JSON_PROPERTY_VALUE_NO_QUOTES, "item", m.asString()), IssueSeverity.ERROR); 638 } 639 parseChildPrimitiveInstance(errors, element, property, name, isJsonName, npath, fpath, m, f); 640 } 641 } 642 } else { 643 parseChildPrimitiveInstance(errors, element, property, name, isJsonName, npath, fpath, main == null ? null : main.getValue(), fork == null ? null : fork.getValue()); 644 } 645 } 646 } 647 648 private JsonElement arrI(JsonArray arr, int i) { 649 return arr == null || i >= arr.size() || arr.get(i) instanceof JsonNull ? null : arr.get(i); 650 } 651 652 private int arrC(JsonArray arr) { 653 return arr == null ? 0 : arr.size(); 654 } 655 656 private void parseChildPrimitiveInstance(List<ValidationMessage> errors, Element element, Property property, String name, boolean isJsonName, String npath, String fpath, JsonElement main, JsonElement fork) throws FHIRException { 657 if (main != null && !(main.isJsonBoolean() || main.isJsonNumber() || main.isJsonString())) { 658 logError(errors, ValidationMessage.NO_RULE_DATE, line(main), col(main), npath, IssueType.INVALID, context.formatMessage( 659 I18nConstants.THIS_PROPERTY_MUST_BE_AN_SIMPLE_VALUE_NOT_, describe(main), name, npath), IssueSeverity.ERROR); 660 } else if (fork != null && !(fork instanceof JsonObject)) { 661 logError(errors, ValidationMessage.NO_RULE_DATE, line(fork), col(fork), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_AN_OBJECT_NOT_, describe(fork), name, npath), IssueSeverity.ERROR); 662 } else { 663 Element n = new Element(isJsonName ? property.getName() : name, property).markLocation(line(main != null ? main : fork), col(main != null ? main : fork)).setFormat(FhirFormat.JSON); 664 if (main != null) { 665 checkComments(errors, main, n, npath); 666 } 667 if (fork != null) { 668 checkComments(errors, fork, n, npath); 669 } 670 n.setPath(fpath); 671 element.getChildren().add(n); 672 if (main != null) { 673 JsonPrimitive p = (JsonPrimitive) main; 674 n.setValue(property.hasImpliedPrefix() ? property.getImpliedPrefix()+p.asString() : p.asString()); 675 if (!n.getProperty().isChoice() && n.getType().equals("xhtml")) { 676 try { 677 XhtmlParser xhtml = new XhtmlParser(); 678 n.setXhtml(xhtml.setXmlMode(true).parse(n.getValue(), null).getDocumentElement()); 679 if (policy == ValidationPolicy.EVERYTHING) { 680 for (StringPair s : xhtml.getValidationIssues()) { 681 logError(errors, "2022-11-17", line(main), col(main), npath, IssueType.INVALID, context.formatMessage(s.getName(), s.getValue()), IssueSeverity.ERROR); 682 } 683 } 684 } catch (Exception e) { 685 logError(errors, ValidationMessage.NO_RULE_DATE, line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_XHTML_, e.getMessage()), IssueSeverity.ERROR); 686 } 687 } 688 if (policy == ValidationPolicy.EVERYTHING) { 689 // now we cross-check the primitive format against the stated type 690 if (Utilities.existsInList(n.getType(), "boolean")) { 691 if (!p.isJsonBoolean()) { 692 logError(errors, ValidationMessage.NO_RULE_DATE, line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_JSON_THE_PRIMITIVE_VALUE_MUST_BE_A_BOOLEAN), IssueSeverity.ERROR); 693 } 694 } else if (Utilities.existsInList(n.getType(), "integer", "unsignedInt", "positiveInt", "decimal")) { 695 if (!p.isJsonNumber()) 696 logError(errors, ValidationMessage.NO_RULE_DATE, line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_JSON_THE_PRIMITIVE_VALUE_MUST_BE_A_NUMBER), IssueSeverity.ERROR); 697 } else if (!p.isJsonString()) { 698 logError(errors, ValidationMessage.NO_RULE_DATE, line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_JSON_THE_PRIMITIVE_VALUE_MUST_BE_A_STRING), IssueSeverity.ERROR); 699 } 700 } 701 } 702 if (fork != null) { 703 JsonObject child = (JsonObject) fork; 704 checkObject(errors, child, n, npath); 705 parseChildren(errors, npath, child, n, false, null); 706 } 707 } 708 } 709 710 711 private void parseResource(List<ValidationMessage> errors, String npath, JsonObject res, Element parent, Property elementProperty) throws FHIRException { 712 JsonElement rt = res.get("resourceType"); 713 if (rt == null) { 714 logError(errors, ValidationMessage.NO_RULE_DATE, line(res), col(res), npath, IssueType.INVALID, context.formatMessage(I18nConstants.UNABLE_TO_FIND_RESOURCETYPE_PROPERTY), IssueSeverity.FATAL); 715 } else if (!rt.isJsonString()) { 716 logError(errors, "2022-11-26", line(res), col(res), npath, IssueType.INVALID, context.formatMessage(I18nConstants.RESOURCETYPE_PROPERTY_WRONG_TYPE, rt.type().toName()), IssueSeverity.FATAL); 717 } else { 718 String name = rt.asString(); 719 StructureDefinition sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, null)); 720 if (sd == null) { 721 logError(errors, ValidationMessage.NO_RULE_DATE, line(res), col(res), npath, IssueType.INVALID, context.formatMessage(I18nConstants.CONTAINED_RESOURCE_DOES_NOT_APPEAR_TO_BE_A_FHIR_RESOURCE_UNKNOWN_NAME_, name), IssueSeverity.FATAL); 722 } else { 723 parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, this.getProfileUtilities(), this.getContextUtilities()), SpecialElement.fromProperty(parent.getProperty()), elementProperty); 724 parent.setType(name); 725 parseChildren(errors, npath, res, parent, true, null); 726 } 727 } 728 if (res.isExtraComma()) { 729 logError(errors, "2022-11-26", res.getEnd().getLine(), res.getEnd().getCol(), npath, IssueType.INVALID, context.formatMessage(I18nConstants.JSON_COMMA_EXTRA, "Object"), IssueSeverity.ERROR); 730 } 731 732 } 733 734 private int line(JsonElement e) { 735 return e.getStart().getLine(); 736 } 737 738 private int col(JsonElement e) { 739 return e.getEnd().getCol(); 740 } 741 742 743 protected void prop(String name, String value, String link) throws IOException { 744 json.link(link); 745 if (name != null) 746 json.name(name); 747 json.value(value); 748 } 749 750 protected void open(String name, String link) throws IOException { 751 json.link(link); 752 if (name != null) 753 json.name(name); 754 json.beginObject(); 755 } 756 757 protected void close() throws IOException { 758 json.endObject(); 759 } 760 761 protected void openArray(String name, String link) throws IOException { 762 json.link(link); 763 if (name != null) 764 json.name(name); 765 json.beginArray(); 766 } 767 768 protected void closeArray() throws IOException { 769 json.endArray(); 770 } 771 772 773 @Override 774 public void compose(Element e, OutputStream stream, OutputStyle style, String identity) throws FHIRException, IOException { 775 if (e.getPath() == null) { 776 e.populatePaths(null); 777 } 778 779 markedXhtml = false; 780 OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8"); 781 if (style == OutputStyle.CANONICAL) { 782 json = new JsonCreatorCanonical(osw); 783 } else if (style == OutputStyle.PRETTY) { 784 json = new JsonCreatorDirect(osw, true, allowComments); 785 } else { 786 json = new JsonCreatorDirect(osw, false, allowComments); 787 } 788 checkComposeComments(e); 789 json.beginObject(); 790// if (!isSuppressResourceType()) 791 prop("resourceType", e.getType(), null); 792 Set<String> done = new HashSet<String>(); 793 for (Element child : e.getChildren()) { 794 compose(e.getName(), e, done, child); 795 } 796 json.endObject(); 797 json.finish(); 798 osw.flush(); 799 } 800 801 private void checkComposeComments(Element e) { 802 for (String s : e.getComments()) { 803 json.comment(s); 804 } 805 } 806 807 public void compose(Element e, JsonCreator json) throws Exception { 808 if (e.getPath() == null) { 809 e.populatePaths(null); 810 } 811 812 this.json = json; 813 checkComposeComments(e); 814 json.beginObject(); 815 816// if (!isSuppressResourceType()) 817 prop("resourceType", e.getType(), linkResolver == null ? null : linkResolver.resolveProperty(e.getProperty())); 818 Set<String> done = new HashSet<String>(); 819 for (Element child : e.getChildren()) { 820 compose(e.getName(), e, done, child); 821 } 822 json.endObject(); 823 json.finish(); 824 } 825 826 private void compose(String path, Element e, Set<String> done, Element child) throws IOException { 827 checkComposeComments(child); 828 if (wantCompose(path, child)) { 829 boolean isList = child.hasElementProperty() ? child.getElementProperty().isList() : child.getProperty().isList(); 830 if (!isList) {// for specials, ignore the cardinality of the stated type 831 if (child.isElided() && isElideElements() && json.canElide()) 832 json.elide(); 833 else 834 compose(path, child); 835 } else if (!done.contains(child.getName())) { 836 done.add(child.getName()); 837 List<Element> list = e.getChildrenByName(child.getName()); 838 boolean skipList = false; 839 if (json.canElide() && isElideElements()) { 840 boolean foundNonElide = false; 841 for (Element listElement: list) { 842 if (!listElement.isElided()) { 843 foundNonElide = true; 844 break; 845 } 846 } 847 if (!foundNonElide) { 848 json.elide(); 849 skipList = true; 850 } 851 } 852 if (!skipList) { 853 if (child.getProperty().getDefinition().hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY)) 854 composeKeyList(path, list); 855 else 856 composeList(path, list); 857 } 858 } 859 } 860 } 861 862 private void composeKeyList(String path, List<Element> list) throws IOException { 863 String keyName = list.get(0).getProperty().getDefinition().getExtensionString(ToolingExtensions.EXT_JSON_PROP_KEY); 864 json.name(list.get(0).getName()); 865 json.beginObject(); 866 for (Element e: list) { 867 Element key = null; 868 Element value = null; 869 for (Element child: e.getChildren()) { 870 if (child.getName().equals(keyName)) 871 key = child; 872 else 873 value = child; 874 } 875 if (value.isPrimitive()) 876 primitiveValue(key.getValue(), value); 877 else { 878 json.name(key.getValue()); 879 checkComposeComments(e); 880 json.beginObject(); 881 Set<String> done = new HashSet<String>(); 882 for (Element child : value.getChildren()) { 883 compose(value.getName(), value, done, child); 884 } 885 json.endObject(); 886 compose(path + "." + key.getValue(), value); 887 } 888 } 889 json.endObject(); 890 } 891 892 private void composeList(String path, List<Element> list) throws IOException { 893 // there will be at least one element 894 String name = list.get(0).getName(); 895 boolean complex = true; 896 if (list.get(0).isPrimitive()) { 897 boolean prim = false; 898 complex = false; 899 for (Element item : list) { 900 if (item.hasValue()) 901 prim = true; 902 if (item.hasChildren()) 903 complex = true; 904 } 905 if (prim) { 906 openArray(name, linkResolver == null ? null : linkResolver.resolveProperty(list.get(0).getProperty())); 907 for (Element item : list) { 908 if (item.isElided() && json.canElide()) 909 json.elide(); 910 else if (item.hasValue()) { 911 if (linkResolver != null && item.getProperty().isReference()) { 912 String ref = linkResolver.resolveReference(getReferenceForElement(item)); 913 if (ref != null) { 914 json.externalLink(ref); 915 } 916 } 917 primitiveValue(null, item); 918 } else 919 json.nullValue(); 920 } 921 closeArray(); 922 } 923 name = "_"+name; 924 } 925 if (complex) { 926 openArray(name, linkResolver == null ? null : linkResolver.resolveProperty(list.get(0).getProperty())); 927 int i = 0; 928 for (Element item : list) { 929 if (item.isElided() && json.canElide()) 930 json.elide(); 931 else if (item.hasChildren()) { 932 open(null,null); 933 if (item.getProperty().isResource()) { 934 prop("resourceType", item.getType(), linkResolver == null ? null : linkResolver.resolveType(item.getType())); 935 } 936 if (linkResolver != null && item.getProperty().isReference()) { 937 String ref = linkResolver.resolveReference(getReferenceForElement(item)); 938 if (ref != null) { 939 json.externalLink(ref); 940 } 941 } 942 Set<String> done = new HashSet<String>(); 943 for (Element child : item.getChildren()) { 944 compose(path+"."+name+"[]", item, done, child); 945 } 946 close(); 947 } else { 948 json.nullValue(); 949 } 950 i++; 951 } 952 closeArray(); 953 } 954 } 955 956 private void primitiveValue(String name, Element item) throws IOException { 957 if (name != null) { 958 if (linkResolver != null) 959 json.link(linkResolver.resolveProperty(item.getProperty())); 960 json.name(name); 961 } 962 String type = item.getType(); 963 if (Utilities.existsInList(type, "boolean")) 964 json.value(item.getValue().trim().equals("true") ? new Boolean(true) : new Boolean(false)); 965 else if (Utilities.existsInList(type, "integer", "unsignedInt", "positiveInt")) 966 json.value(new Integer(item.getValue())); 967 else if (Utilities.existsInList(type, "decimal")) 968 try { 969 json.value(new BigDecimal(item.getValue())); 970 } catch (Exception e) { 971 throw new NumberFormatException(context.formatMessage(I18nConstants.ERROR_WRITING_NUMBER__TO_JSON, item.getValue())); 972 } 973 else 974 json.value(item.getValue()); 975 } 976 977 private void compose(String path, Element element) throws IOException { 978 String name = element.getName(); 979 if (element.isPrimitive() || isPrimitive(element.getType())) { 980 if (element.hasValue()) 981 primitiveValue(name, element); 982 name = "_"+name; 983 if (!markedXhtml && element.getType().equals("xhtml")) 984 json.anchor("end-xhtml"); 985 markedXhtml = true; 986 } 987 if (element.hasChildren()) { 988 open(name, linkResolver == null ? null : linkResolver.resolveProperty(element.getProperty())); 989 if (element.getProperty().isResource()) { 990 prop("resourceType", element.getType(), linkResolver == null ? null : linkResolver.resolveType(element.getType())); 991 } 992 if (linkResolver != null && element.getProperty().isReference()) { 993 String ref = linkResolver.resolveReference(getReferenceForElement(element)); 994 if (ref != null) { 995 json.externalLink(ref); 996 } 997 } 998 999 Set<String> done = new HashSet<String>(); 1000 for (Element child : element.getChildren()) { 1001 compose(path + "." + element.getName(), element, done, child); 1002 } 1003 close(); 1004 } 1005 } 1006 1007 1008 public boolean isAllowComments() { 1009 return allowComments; 1010 } 1011 1012 public JsonParser setAllowComments(boolean allowComments) { 1013 this.allowComments = allowComments; 1014 return this; 1015 } 1016 1017 public boolean isElideElements() { 1018 return elideElements; 1019 } 1020 1021 public JsonParser setElideElements(boolean elideElements) { 1022 this.elideElements = elideElements; 1023 return this; 1024 } 1025/* 1026 public boolean isSuppressResourceType() { 1027 return suppressResourceType; 1028 } 1029 1030 public JsonParser setSuppressResourceType(boolean suppressResourceType) { 1031 this.suppressResourceType = suppressResourceType; 1032 return this; 1033 } 1034*/ 1035 1036}