001package ca.uhn.fhir.parser; 002 003/* 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2016 University Health Network 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import static org.apache.commons.lang3.StringUtils.defaultString; 024import static org.apache.commons.lang3.StringUtils.isBlank; 025import static org.apache.commons.lang3.StringUtils.isNotBlank; 026 027import java.io.IOException; 028import java.io.Reader; 029import java.io.Writer; 030import java.math.BigDecimal; 031import java.util.ArrayList; 032import java.util.Collections; 033import java.util.HashMap; 034import java.util.HashSet; 035import java.util.Iterator; 036import java.util.List; 037import java.util.Map; 038import java.util.Map.Entry; 039import java.util.Set; 040 041import javax.json.Json; 042import javax.json.JsonArray; 043import javax.json.JsonNumber; 044import javax.json.JsonObject; 045import javax.json.JsonReader; 046import javax.json.JsonString; 047import javax.json.JsonValue; 048import javax.json.JsonValue.ValueType; 049import javax.json.stream.JsonGenerator; 050import javax.json.stream.JsonGeneratorFactory; 051import javax.json.stream.JsonParsingException; 052 053import org.apache.commons.lang3.StringUtils; 054import org.hl7.fhir.instance.model.api.IBase; 055import org.hl7.fhir.instance.model.api.IBaseBinary; 056import org.hl7.fhir.instance.model.api.IBaseBooleanDatatype; 057import org.hl7.fhir.instance.model.api.IBaseDatatype; 058import org.hl7.fhir.instance.model.api.IBaseDecimalDatatype; 059import org.hl7.fhir.instance.model.api.IBaseExtension; 060import org.hl7.fhir.instance.model.api.IBaseHasExtensions; 061import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions; 062import org.hl7.fhir.instance.model.api.IBaseIntegerDatatype; 063import org.hl7.fhir.instance.model.api.IBaseReference; 064import org.hl7.fhir.instance.model.api.IBaseResource; 065import org.hl7.fhir.instance.model.api.IDomainResource; 066import org.hl7.fhir.instance.model.api.IIdType; 067import org.hl7.fhir.instance.model.api.INarrative; 068import org.hl7.fhir.instance.model.api.IPrimitiveType; 069 070import ca.uhn.fhir.context.BaseRuntimeChildDefinition; 071import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; 072import ca.uhn.fhir.context.BaseRuntimeElementDefinition; 073import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum; 074import ca.uhn.fhir.context.ConfigurationException; 075import ca.uhn.fhir.context.FhirContext; 076import ca.uhn.fhir.context.FhirVersionEnum; 077import ca.uhn.fhir.context.RuntimeChildContainedResources; 078import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition; 079import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition; 080import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition; 081import ca.uhn.fhir.context.RuntimeResourceDefinition; 082import ca.uhn.fhir.model.api.BaseBundle; 083import ca.uhn.fhir.model.api.Bundle; 084import ca.uhn.fhir.model.api.BundleEntry; 085import ca.uhn.fhir.model.api.ExtensionDt; 086import ca.uhn.fhir.model.api.IIdentifiableElement; 087import ca.uhn.fhir.model.api.IPrimitiveDatatype; 088import ca.uhn.fhir.model.api.IResource; 089import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions; 090import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; 091import ca.uhn.fhir.model.api.Tag; 092import ca.uhn.fhir.model.api.TagList; 093import ca.uhn.fhir.model.api.annotation.Child; 094import ca.uhn.fhir.model.base.composite.BaseCodingDt; 095import ca.uhn.fhir.model.base.composite.BaseContainedDt; 096import ca.uhn.fhir.model.base.composite.BaseNarrativeDt; 097import ca.uhn.fhir.model.primitive.DecimalDt; 098import ca.uhn.fhir.model.primitive.IdDt; 099import ca.uhn.fhir.model.primitive.InstantDt; 100import ca.uhn.fhir.model.primitive.IntegerDt; 101import ca.uhn.fhir.model.primitive.StringDt; 102import ca.uhn.fhir.narrative.INarrativeGenerator; 103import ca.uhn.fhir.rest.server.EncodingEnum; 104import ca.uhn.fhir.util.ElementUtil; 105 106/** 107 * This class is the FHIR JSON parser/encoder. Users should not interact with this class directly, but should use 108 * {@link FhirContext#newJsonParser()} to get an instance. 109 */ 110public class JsonParser extends BaseParser implements IParser { 111 112 private static final Set<String> BUNDLE_TEXTNODE_CHILDREN_DSTU1; 113 private static final Set<String> BUNDLE_TEXTNODE_CHILDREN_DSTU2; 114 private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParser.HeldExtension.class); 115 116 static { 117 HashSet<String> hashSetDstu1 = new HashSet<String>(); 118 hashSetDstu1.add("title"); 119 hashSetDstu1.add("id"); 120 hashSetDstu1.add("updated"); 121 hashSetDstu1.add("published"); 122 hashSetDstu1.add("totalResults"); 123 BUNDLE_TEXTNODE_CHILDREN_DSTU1 = Collections.unmodifiableSet(hashSetDstu1); 124 125 HashSet<String> hashSetDstu2 = new HashSet<String>(); 126 hashSetDstu2.add("type"); 127 hashSetDstu2.add("base"); 128 hashSetDstu2.add("total"); 129 BUNDLE_TEXTNODE_CHILDREN_DSTU2 = Collections.unmodifiableSet(hashSetDstu2); 130 } 131 132 private FhirContext myContext; 133 private boolean myPrettyPrint; 134 135 /** 136 * Do not use this constructor, the recommended way to obtain a new instance of the JSON parser is to invoke 137 * {@link FhirContext#newJsonParser()}. 138 * 139 * @param theParserErrorHandler 140 */ 141 public JsonParser(FhirContext theContext, IParserErrorHandler theParserErrorHandler) { 142 super(theContext, theParserErrorHandler); 143 myContext = theContext; 144 } 145 146 private void addToHeldExtensions(int valueIdx, List<? extends IBaseExtension<?, ?>> ext, ArrayList<ArrayList<HeldExtension>> list, boolean theIsModifier) { 147 if (ext.size() > 0) { 148 list.ensureCapacity(valueIdx); 149 while (list.size() <= valueIdx) { 150 list.add(null); 151 } 152 if (list.get(valueIdx) == null) { 153 list.set(valueIdx, new ArrayList<JsonParser.HeldExtension>()); 154 } 155 for (IBaseExtension<?, ?> next : ext) { 156 list.get(valueIdx).add(new HeldExtension(next, theIsModifier)); 157 } 158 } 159 } 160 161 private void assertObjectOfType(JsonValue theResourceTypeObj, ValueType theValueType, String thePosition) { 162 if (theResourceTypeObj == null) { 163 throw new DataFormatException("Invalid JSON content detected, missing required element: '" + thePosition + "'"); 164 } 165 166 if (theResourceTypeObj.getValueType() != theValueType) { 167 throw new DataFormatException("Invalid content of element " + thePosition + ", expected " + theValueType); 168 } 169 } 170 171 private JsonGenerator createJsonGenerator(Writer theWriter) { 172 Map<String, Object> properties = new HashMap<String, Object>(1); 173 if (myPrettyPrint) { 174 properties.put(JsonGenerator.PRETTY_PRINTING, myPrettyPrint); 175 } 176 JsonGeneratorFactory jgf = Json.createGeneratorFactory(properties); 177 JsonGenerator eventWriter = jgf.createGenerator(theWriter); 178 return eventWriter; 179 } 180 181 @Override 182 public void doEncodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException { 183 JsonGenerator eventWriter = createJsonGenerator(theWriter); 184 if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { 185 encodeBundleToWriterInDstu2Format(theBundle, eventWriter); 186 } else { 187 encodeBundleToWriterInDstu1Format(theBundle, eventWriter); 188 } 189 eventWriter.flush(); 190 } 191 192 @Override 193 protected void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws IOException { 194 JsonGenerator eventWriter = createJsonGenerator(theWriter); 195 196 RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource); 197 encodeResourceToJsonStreamWriter(resDef, theResource, eventWriter, null, false); 198 eventWriter.flush(); 199 } 200 201 @Override 202 public <T extends IBaseResource> T doParseResource(Class<T> theResourceType, Reader theReader) { 203 try { 204 JsonReader reader = Json.createReader(theReader); 205 JsonObject object = reader.readObject(); 206 207 JsonValue resourceTypeObj = object.get("resourceType"); 208 assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType"); 209 String resourceType = ((JsonString) resourceTypeObj).getString(); 210 211 ParserState<? extends IBaseResource> state = ParserState.getPreResourceInstance(theResourceType, myContext, true, getErrorHandler()); 212 state.enteringNewElement(null, resourceType); 213 214 parseChildren(object, state); 215 216 state.endingElement(); 217 218 @SuppressWarnings("unchecked") 219 T retVal = (T) state.getObject(); 220 221 return retVal; 222 } catch (JsonParsingException e) { 223 throw new DataFormatException("Failed to parse JSON: " + e.getMessage(), e); 224 } 225 } 226 227 private void encodeBundleToWriterInDstu1Format(Bundle theBundle, JsonGenerator eventWriter) throws IOException { 228 eventWriter.writeStartObject(); 229 230 eventWriter.write("resourceType", "Bundle"); 231 232 writeTagWithTextNode(eventWriter, "title", theBundle.getTitle()); 233 writeTagWithTextNode(eventWriter, "id", theBundle.getBundleId()); 234 writeOptionalTagWithTextNode(eventWriter, "updated", theBundle.getUpdated()); 235 236 boolean linkStarted = false; 237 linkStarted = writeAtomLinkInDstu1Format(eventWriter, "self", theBundle.getLinkSelf(), linkStarted); 238 linkStarted = writeAtomLinkInDstu1Format(eventWriter, "first", theBundle.getLinkFirst(), linkStarted); 239 linkStarted = writeAtomLinkInDstu1Format(eventWriter, "previous", theBundle.getLinkPrevious(), linkStarted); 240 linkStarted = writeAtomLinkInDstu1Format(eventWriter, "next", theBundle.getLinkNext(), linkStarted); 241 linkStarted = writeAtomLinkInDstu1Format(eventWriter, "last", theBundle.getLinkLast(), linkStarted); 242 linkStarted = writeAtomLinkInDstu1Format(eventWriter, "fhir-base", theBundle.getLinkBase(), linkStarted); 243 if (linkStarted) { 244 eventWriter.writeEnd(); 245 } 246 247 writeCategories(eventWriter, theBundle.getCategories()); 248 249 writeOptionalTagWithTextNode(eventWriter, "totalResults", theBundle.getTotalResults()); 250 251 writeAuthor(theBundle, eventWriter); 252 253 eventWriter.writeStartArray("entry"); 254 for (BundleEntry nextEntry : theBundle.getEntries()) { 255 eventWriter.writeStartObject(); 256 257 boolean deleted = nextEntry.getDeletedAt() != null && nextEntry.getDeletedAt().isEmpty() == false; 258 if (deleted) { 259 writeTagWithTextNode(eventWriter, "deleted", nextEntry.getDeletedAt()); 260 } 261 writeTagWithTextNode(eventWriter, "title", nextEntry.getTitle()); 262 writeTagWithTextNode(eventWriter, "id", nextEntry.getId()); 263 264 linkStarted = false; 265 linkStarted = writeAtomLinkInDstu1Format(eventWriter, "self", nextEntry.getLinkSelf(), linkStarted); 266 linkStarted = writeAtomLinkInDstu1Format(eventWriter, "alternate", nextEntry.getLinkAlternate(), linkStarted); 267 linkStarted = writeAtomLinkInDstu1Format(eventWriter, "search", nextEntry.getLinkSearch(), linkStarted); 268 if (linkStarted) { 269 eventWriter.writeEnd(); 270 } 271 272 writeOptionalTagWithTextNode(eventWriter, "updated", nextEntry.getUpdated()); 273 writeOptionalTagWithTextNode(eventWriter, "published", nextEntry.getPublished()); 274 275 writeCategories(eventWriter, nextEntry.getCategories()); 276 277 writeAuthor(nextEntry, eventWriter); 278 279 IResource resource = nextEntry.getResource(); 280 if (resource != null && !resource.isEmpty() && !deleted) { 281 RuntimeResourceDefinition resDef = myContext.getResourceDefinition(resource); 282 encodeResourceToJsonStreamWriter(resDef, resource, eventWriter, "content", false); 283 } 284 285 if (nextEntry.getSummary().isEmpty() == false) { 286 eventWriter.write("summary", nextEntry.getSummary().getValueAsString()); 287 } 288 289 eventWriter.writeEnd(); // entry object 290 } 291 eventWriter.writeEnd(); // entry array 292 293 eventWriter.writeEnd(); 294 } 295 296 private void encodeBundleToWriterInDstu2Format(Bundle theBundle, JsonGenerator theEventWriter) throws IOException { 297 theEventWriter.writeStartObject(); 298 299 theEventWriter.write("resourceType", "Bundle"); 300 301 writeOptionalTagWithTextNode(theEventWriter, "id", theBundle.getId().getIdPart()); 302 303 if (!ElementUtil.isEmpty(theBundle.getId().getVersionIdPart(), theBundle.getUpdated())) { 304 theEventWriter.writeStartObject("meta"); 305 writeOptionalTagWithTextNode(theEventWriter, "versionId", theBundle.getId().getVersionIdPart()); 306 writeOptionalTagWithTextNode(theEventWriter, "lastUpdated", theBundle.getUpdated()); 307 theEventWriter.writeEnd(); 308 } 309 310 writeOptionalTagWithTextNode(theEventWriter, "type", theBundle.getType()); 311 312 writeOptionalTagWithNumberNode(theEventWriter, "total", theBundle.getTotalResults()); 313 314 boolean linkStarted = false; 315 linkStarted = writeAtomLinkInDstu2Format(theEventWriter, "next", theBundle.getLinkNext(), linkStarted); 316 linkStarted = writeAtomLinkInDstu2Format(theEventWriter, "self", theBundle.getLinkSelf(), linkStarted); 317 linkStarted = writeAtomLinkInDstu2Format(theEventWriter, "first", theBundle.getLinkFirst(), linkStarted); 318 linkStarted = writeAtomLinkInDstu2Format(theEventWriter, "previous", theBundle.getLinkPrevious(), linkStarted); 319 linkStarted = writeAtomLinkInDstu2Format(theEventWriter, "last", theBundle.getLinkLast(), linkStarted); 320 if (linkStarted) { 321 theEventWriter.writeEnd(); 322 } 323 324 theEventWriter.writeStartArray("entry"); 325 for (BundleEntry nextEntry : theBundle.getEntries()) { 326 theEventWriter.writeStartObject(); 327 328 if (nextEntry.getResource() != null && nextEntry.getResource().getId().getBaseUrl() != null) { 329 writeOptionalTagWithTextNode(theEventWriter, "fullUrl", nextEntry.getResource().getId().getValue()); 330 } 331 332 boolean deleted = nextEntry.getDeletedAt() != null && nextEntry.getDeletedAt().isEmpty() == false; 333 IResource resource = nextEntry.getResource(); 334 if (resource != null && !resource.isEmpty() && !deleted) { 335 RuntimeResourceDefinition resDef = myContext.getResourceDefinition(resource); 336 encodeResourceToJsonStreamWriter(resDef, resource, theEventWriter, "resource", false); 337 } 338 339 if (nextEntry.getSearchMode().isEmpty() == false || nextEntry.getScore().isEmpty() == false) { 340 theEventWriter.writeStartObject("search"); 341 writeOptionalTagWithTextNode(theEventWriter, "mode", nextEntry.getSearchMode().getValueAsString()); 342 writeOptionalTagWithDecimalNode(theEventWriter, "score", nextEntry.getScore()); 343 theEventWriter.writeEnd(); 344 // IResource nextResource = nextEntry.getResource(); 345 } 346 347 if (nextEntry.getTransactionMethod().isEmpty() == false || nextEntry.getLinkSearch().isEmpty() == false) { 348 theEventWriter.writeStartObject("request"); 349 writeOptionalTagWithTextNode(theEventWriter, "method", nextEntry.getTransactionMethod().getValue()); 350 writeOptionalTagWithTextNode(theEventWriter, "url", nextEntry.getLinkSearch().getValue()); 351 theEventWriter.writeEnd(); 352 } 353 354 if (deleted) { 355 theEventWriter.writeStartObject("deleted"); 356 if (nextEntry.getResource() != null) { 357 theEventWriter.write("type", myContext.getResourceDefinition(nextEntry.getResource()).getName()); 358 writeOptionalTagWithTextNode(theEventWriter, "resourceId", nextEntry.getResource().getId().getIdPart()); 359 writeOptionalTagWithTextNode(theEventWriter, "versionId", nextEntry.getResource().getId().getVersionIdPart()); 360 } 361 writeTagWithTextNode(theEventWriter, "instant", nextEntry.getDeletedAt()); 362 theEventWriter.writeEnd(); 363 } 364 365 // linkStarted = false; 366 // linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "self", nextEntry.getLinkSelf(), linkStarted); 367 // linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "alternate", nextEntry.getLinkAlternate(), 368 // linkStarted); 369 // linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "search", nextEntry.getLinkSearch(), 370 // linkStarted); 371 // if (linkStarted) { 372 // theEventWriter.writeEnd(); 373 // } 374 // 375 // writeOptionalTagWithTextNode(theEventWriter, "updated", nextEntry.getUpdated()); 376 // writeOptionalTagWithTextNode(theEventWriter, "published", nextEntry.getPublished()); 377 // 378 // writeCategories(theEventWriter, nextEntry.getCategories()); 379 // 380 // writeAuthor(nextEntry, theEventWriter); 381 382 if (nextEntry.getSummary().isEmpty() == false) { 383 theEventWriter.write("summary", nextEntry.getSummary().getValueAsString()); 384 } 385 386 theEventWriter.writeEnd(); // entry object 387 } 388 theEventWriter.writeEnd(); // entry array 389 390 theEventWriter.writeEnd(); 391 } 392 393 private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theWriter, IBase theNextValue, BaseRuntimeElementDefinition<?> theChildDef, String theChildName, boolean theContainedResource, CompositeChildElement theChildElem) 394 throws IOException { 395 396 switch (theChildDef.getChildType()) { 397 case ID_DATATYPE: { 398 IIdType value = (IIdType) theNextValue; 399 String encodedValue = "id".equals(theChildName) ? value.getIdPart() : value.getValue(); 400 if (isBlank(encodedValue)) { 401 break; 402 } 403 if (theChildName != null) { 404 theWriter.write(theChildName, encodedValue); 405 } else { 406 theWriter.write(encodedValue); 407 } 408 break; 409 } 410 case PRIMITIVE_DATATYPE: { 411 final IPrimitiveType<?> value = (IPrimitiveType<?>) theNextValue; 412 if (isBlank(value.getValueAsString())) { 413 break; 414 } 415 416 if (value instanceof IBaseIntegerDatatype) { 417 if (theChildName != null) { 418 theWriter.write(theChildName, ((IBaseIntegerDatatype) value).getValue()); 419 } else { 420 theWriter.write(((IBaseIntegerDatatype) value).getValue()); 421 } 422 } else if (value instanceof IBaseDecimalDatatype) { 423 BigDecimal decimalValue = ((IBaseDecimalDatatype) value).getValue(); 424 decimalValue = new BigDecimal(decimalValue.toString()) { 425 private static final long serialVersionUID = 1L; 426 427 @Override 428 public String toString() { 429 return value.getValueAsString(); 430 }}; 431 if (theChildName != null) { 432 theWriter.write(theChildName, decimalValue); 433 } else { 434 theWriter.write(decimalValue); 435 } 436 } else if (value instanceof IBaseBooleanDatatype) { 437 if (theChildName != null) { 438 theWriter.write(theChildName, ((IBaseBooleanDatatype) value).getValue()); 439 } else { 440 theWriter.write(((IBaseBooleanDatatype) value).getValue()); 441 } 442 } else { 443 String valueStr = value.getValueAsString(); 444 if (theChildName != null) { 445 theWriter.write(theChildName, valueStr); 446 } else { 447 theWriter.write(valueStr); 448 } 449 } 450 break; 451 } 452 case RESOURCE_BLOCK: 453 case COMPOSITE_DATATYPE: { 454 BaseRuntimeElementCompositeDefinition<?> childCompositeDef = (BaseRuntimeElementCompositeDefinition<?>) theChildDef; 455 if (theChildName != null) { 456 theWriter.writeStartObject(theChildName); 457 } else { 458 theWriter.writeStartObject(); 459 } 460 if (theNextValue instanceof IBaseExtension) { 461 theWriter.write("url", ((IBaseExtension<?, ?>) theNextValue).getUrl()); 462 } 463 encodeCompositeElementToStreamWriter(theResDef, theResource, theNextValue, theWriter, childCompositeDef, theContainedResource, theChildElem); 464 theWriter.writeEnd(); 465 break; 466 } 467 case RESOURCE_REF: { 468 IBaseReference referenceDt = (IBaseReference) theNextValue; 469 if (theChildName != null) { 470 theWriter.writeStartObject(theChildName); 471 } else { 472 theWriter.writeStartObject(); 473 } 474 475 String reference = determineReferenceText(referenceDt); 476 477 if (StringUtils.isNotBlank(reference)) { 478 theWriter.write(XmlParser.RESREF_REFERENCE, reference); 479 } 480 if (referenceDt.getDisplayElement().isEmpty() == false) { 481 theWriter.write(XmlParser.RESREF_DISPLAY, referenceDt.getDisplayElement().getValueAsString()); 482 } 483 theWriter.writeEnd(); 484 break; 485 } 486 case CONTAINED_RESOURCE_LIST: 487 case CONTAINED_RESOURCES: { 488 /* 489 * Disabled per #103 ContainedDt value = (ContainedDt) theNextValue; for (IResource next : 490 * value.getContainedResources()) { if (getContainedResources().getResourceId(next) != null) { continue; } 491 * encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true, 492 * fixContainedResourceId(next.getId().getValue())); } 493 */ 494 List<IBaseResource> containedResources = getContainedResources().getContainedResources(); 495 if (containedResources.size() > 0) { 496 theWriter.writeStartArray(theChildName); 497 498 for (IBaseResource next : containedResources) { 499 IIdType resourceId = getContainedResources().getResourceId(next); 500 encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true, fixContainedResourceId(resourceId.getValue())); 501 } 502 503 theWriter.writeEnd(); 504 } 505 break; 506 } 507 case PRIMITIVE_XHTML_HL7ORG: 508 case PRIMITIVE_XHTML: { 509 if (!isSuppressNarratives()) { 510 IPrimitiveType<?> dt = (IPrimitiveType<?>) theNextValue; 511 if (theChildName != null) { 512 theWriter.write(theChildName, dt.getValueAsString()); 513 } else { 514 theWriter.write(dt.getValueAsString()); 515 } 516 } else { 517 if (theChildName != null) { 518 // do nothing 519 } else { 520 theWriter.writeNull(); 521 } 522 } 523 break; 524 } 525 case RESOURCE: 526 IBaseResource resource = (IBaseResource) theNextValue; 527 RuntimeResourceDefinition def = myContext.getResourceDefinition(resource); 528 encodeResourceToJsonStreamWriter(def, resource, theWriter, theChildName, false); 529 break; 530 case UNDECL_EXT: 531 default: 532 throw new IllegalStateException("Should not have this state here: " + theChildDef.getChildType().name()); 533 } 534 535 } 536 537 private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter, List<? extends BaseRuntimeChildDefinition> theChildren, boolean theContainedResource, CompositeChildElement theParent) 538 throws IOException { 539 for (CompositeChildElement nextChildElem : super.compositeChildIterator(theChildren, theContainedResource, theParent)) { 540 541 BaseRuntimeChildDefinition nextChild = nextChildElem.getDef(); 542 if (nextChild instanceof RuntimeChildNarrativeDefinition) { 543 INarrativeGenerator gen = myContext.getNarrativeGenerator(); 544 if (gen != null) { 545 INarrative narr; 546 if (theResource instanceof IResource) { 547 narr = ((IResource) theResource).getText(); 548 } else if (theResource instanceof IDomainResource) { 549 narr = ((IDomainResource)theResource).getText(); 550 } else { 551 narr = null; 552 } 553 if (narr != null && narr.isEmpty()) { 554 gen.generateNarrative(myContext, theResource, narr); 555 if (narr != null && !narr.isEmpty()) { 556 RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild; 557 String childName = nextChild.getChildNameByDatatype(child.getDatatype()); 558 BaseRuntimeElementDefinition<?> type = child.getChildByName(childName); 559 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, narr, type, childName, theContainedResource, nextChildElem); 560 continue; 561 } 562 } 563 } 564 } else if (nextChild instanceof RuntimeChildContainedResources) { 565 String childName = nextChild.getValidChildNames().iterator().next(); 566 BaseRuntimeElementDefinition<?> child = nextChild.getChildByName(childName); 567 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, null, child, childName, theContainedResource, nextChildElem); 568 continue; 569 } 570 571 List<? extends IBase> values = nextChild.getAccessor().getValues(theNextValue); 572 values = super.preProcessValues(nextChild, theResource, values); 573 574 if (values == null || values.isEmpty()) { 575 continue; 576 } 577 578 String currentChildName = null; 579 boolean inArray = false; 580 581 ArrayList<ArrayList<HeldExtension>> extensions = new ArrayList<ArrayList<HeldExtension>>(0); 582 ArrayList<ArrayList<HeldExtension>> modifierExtensions = new ArrayList<ArrayList<HeldExtension>>(0); 583 584 int valueIdx = 0; 585 for (IBase nextValue : values) { 586 587 if (nextValue == null || nextValue.isEmpty()) { 588 if (nextValue instanceof BaseContainedDt) { 589 if (theContainedResource || getContainedResources().isEmpty()) { 590 continue; 591 } 592 } else { 593 continue; 594 } 595 } 596 597 Class<? extends IBase> type = nextValue.getClass(); 598 String childName = nextChild.getChildNameByDatatype(type); 599 BaseRuntimeElementDefinition<?> childDef = nextChild.getChildElementDefinitionByDatatype(type); 600 if (childDef == null) { 601 super.throwExceptionForUnknownChildType(nextChild, type); 602 } 603 boolean primitive = childDef.getChildType() == ChildTypeEnum.PRIMITIVE_DATATYPE; 604 605 if ((childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCES || childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCE_LIST) && theContainedResource) { 606 continue; 607 } 608 609 if (nextChild instanceof RuntimeChildDeclaredExtensionDefinition) { 610 // Don't encode extensions 611 // RuntimeChildDeclaredExtensionDefinition extDef = (RuntimeChildDeclaredExtensionDefinition) 612 // nextChild; 613 // if (extDef.isModifier()) { 614 // addToHeldExtensions(valueIdx, modifierExtensions, extDef, nextValue); 615 // } else { 616 // addToHeldExtensions(valueIdx, extensions, extDef, nextValue); 617 // } 618 } else { 619 620 if (currentChildName == null || !currentChildName.equals(childName)) { 621 if (inArray) { 622 theEventWriter.writeEnd(); 623 } 624 if (nextChild.getMax() > 1 || nextChild.getMax() == Child.MAX_UNLIMITED) { 625 theEventWriter.writeStartArray(childName); 626 inArray = true; 627 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theContainedResource, nextChildElem); 628 } else if (nextChild instanceof RuntimeChildNarrativeDefinition && theContainedResource) { 629 // suppress narratives from contained resources 630 } else { 631 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, childName, theContainedResource, nextChildElem); 632 } 633 currentChildName = childName; 634 } else { 635 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theContainedResource, nextChildElem); 636 } 637 638 if (primitive) { 639 if (nextValue instanceof ISupportsUndeclaredExtensions) { 640 List<ExtensionDt> ext = ((ISupportsUndeclaredExtensions) nextValue).getUndeclaredExtensions(); 641 addToHeldExtensions(valueIdx, ext, extensions, false); 642 643 ext = ((ISupportsUndeclaredExtensions) nextValue).getUndeclaredModifierExtensions(); 644 addToHeldExtensions(valueIdx, ext, modifierExtensions, true); 645 } else { 646 if (nextValue instanceof IBaseHasExtensions) { 647 IBaseHasExtensions element = (IBaseHasExtensions) nextValue; 648 List<? extends IBaseExtension<?, ?>> ext = element.getExtension(); 649 addToHeldExtensions(valueIdx, ext, extensions, false); 650 } 651 if (nextValue instanceof IBaseHasModifierExtensions) { 652 IBaseHasModifierExtensions element = (IBaseHasModifierExtensions) nextValue; 653 List<? extends IBaseExtension<?, ?>> ext = element.getModifierExtension(); 654 addToHeldExtensions(valueIdx, ext, extensions, true); 655 } 656 } 657 } 658 659 } 660 661 valueIdx++; 662 } 663 664 if (inArray) { 665 theEventWriter.writeEnd(); 666 } 667 668 if (extensions.size() > 0 || modifierExtensions.size() > 0) { 669 if (inArray) { 670 // If this is a repeatable field, the extensions go in an array too 671 theEventWriter.writeStartArray('_' + currentChildName); 672 } else { 673 theEventWriter.writeStartObject('_' + currentChildName); 674 } 675 676 for (int i = 0; i < valueIdx; i++) { 677 boolean haveContent = false; 678 679 List<HeldExtension> heldExts = Collections.emptyList(); 680 List<HeldExtension> heldModExts = Collections.emptyList(); 681 if (extensions.size() > i && extensions.get(i) != null && extensions.get(i).isEmpty() == false) { 682 haveContent = true; 683 heldExts = extensions.get(i); 684 } 685 686 if (modifierExtensions.size() > i && modifierExtensions.get(i) != null && modifierExtensions.get(i).isEmpty() == false) { 687 haveContent = true; 688 heldModExts = modifierExtensions.get(i); 689 } 690 691 if (!haveContent) { 692 theEventWriter.writeNull(); 693 } else { 694 if (inArray) { 695 theEventWriter.writeStartObject(); 696 } 697 writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, heldExts, heldModExts, null); 698 if (inArray) { 699 theEventWriter.writeEnd(); 700 } 701 } 702 } 703 704 theEventWriter.writeEnd(); 705 } 706 } 707 } 708 709 private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef, boolean theContainedResource, CompositeChildElement theParent) 710 throws IOException, DataFormatException { 711 extractAndWriteExtensionsAsDirectChild(theNextValue, theEventWriter, resDef, theResDef, theResource, null); 712 encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, resDef.getExtensions(), theContainedResource, theParent); 713 encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, resDef.getChildren(), theContainedResource, theParent); 714 } 715 716 private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theContainedResource) throws IOException { 717 String resourceId = null; 718// if (theResource instanceof IResource) { 719// IResource res = (IResource) theResource; 720// if (StringUtils.isNotBlank(res.getId().getIdPart())) { 721// if (theContainedResource) { 722// resourceId = res.getId().getIdPart(); 723// } else if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { 724// resourceId = res.getId().getIdPart(); 725// } 726// } 727// } else if (theResource instanceof IAnyResource) { 728// IAnyResource res = (IAnyResource) theResource; 729// if (/* theContainedResource && */StringUtils.isNotBlank(res.getIdElement().getIdPart())) { 730// resourceId = res.getIdElement().getIdPart(); 731// } 732// } 733 734 if (StringUtils.isNotBlank(theResource.getIdElement().getIdPart())) { 735 resourceId = theResource.getIdElement().getIdPart(); 736 if (theResource.getIdElement().getValue().startsWith("urn:")) { 737 resourceId = null; 738 } 739 if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) { 740 resourceId = null; 741 } 742 } 743 744 if (isOmitResourceId() && !theContainedResource) { 745 resourceId = null; 746 } 747 748 encodeResourceToJsonStreamWriter(theResDef, theResource, theEventWriter, theObjectNameOrNull, theContainedResource, resourceId); 749 } 750 751 private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theContainedResource, String theResourceId) throws IOException { 752 if (!theContainedResource) { 753 super.containResourcesForEncoding(theResource); 754 } 755 756 RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource); 757 758 if (theObjectNameOrNull == null) { 759 theEventWriter.writeStartObject(); 760 } else { 761 theEventWriter.writeStartObject(theObjectNameOrNull); 762 } 763 764 theEventWriter.write("resourceType", resDef.getName()); 765 if (theResourceId != null) { 766 theEventWriter.write("id", theResourceId); 767 } 768 769 if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1) && theResource instanceof IResource) { 770 IResource resource = (IResource) theResource; 771 // Object securityLabelRawObj = 772 773 List<BaseCodingDt> securityLabels = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.SECURITY_LABELS); 774 List<IdDt> profiles = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.PROFILES); 775 TagList tags = getMetaTagsForEncoding(resource); 776 InstantDt updated = (InstantDt) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED); 777 IdDt resourceId = resource.getId(); 778 String versionIdPart = resourceId.getVersionIdPart(); 779 if (isBlank(versionIdPart)) { 780 versionIdPart = ResourceMetadataKeyEnum.VERSION.get(resource); 781 } 782 783 if (ElementUtil.isEmpty(versionIdPart, updated, securityLabels, tags, profiles) == false) { 784 theEventWriter.writeStartObject("meta"); 785 writeOptionalTagWithTextNode(theEventWriter, "versionId", versionIdPart); 786 writeOptionalTagWithTextNode(theEventWriter, "lastUpdated", updated); 787 788 if (profiles != null && profiles.isEmpty() == false) { 789 theEventWriter.writeStartArray("profile"); 790 for (IdDt profile : profiles) { 791 if (profile != null && isNotBlank(profile.getValue())) { 792 theEventWriter.write(profile.getValue()); 793 } 794 } 795 theEventWriter.writeEnd(); 796 } 797 798 if (securityLabels.isEmpty() == false) { 799 theEventWriter.writeStartArray("security"); 800 for (BaseCodingDt securityLabel : securityLabels) { 801 theEventWriter.writeStartObject(); 802 BaseRuntimeElementCompositeDefinition<?> def = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(securityLabel.getClass()); 803 encodeCompositeElementChildrenToStreamWriter(resDef, resource, securityLabel, theEventWriter, def.getChildren(), theContainedResource, null); 804 theEventWriter.writeEnd(); 805 } 806 theEventWriter.writeEnd(); 807 } 808 809 if (tags != null && tags.isEmpty() == false) { 810 theEventWriter.writeStartArray("tag"); 811 for (Tag tag : tags) { 812 if (tag.isEmpty()) { 813 continue; 814 } 815 theEventWriter.writeStartObject(); 816 writeOptionalTagWithTextNode(theEventWriter, "system", tag.getScheme()); 817 writeOptionalTagWithTextNode(theEventWriter, "code", tag.getTerm()); 818 writeOptionalTagWithTextNode(theEventWriter, "display", tag.getLabel()); 819 theEventWriter.writeEnd(); 820 } 821 theEventWriter.writeEnd(); 822 } 823 824 theEventWriter.writeEnd(); // end meta 825 } 826 } 827 828 if (theResource instanceof IBaseBinary) { 829 IBaseBinary bin = (IBaseBinary) theResource; 830 String contentType = bin.getContentType(); 831 if (isNotBlank(contentType)) { 832 theEventWriter.write("contentType", contentType); 833 } 834 String contentAsBase64 = bin.getContentAsBase64(); 835 if (isNotBlank(contentAsBase64)) { 836 theEventWriter.write("content", contentAsBase64); 837 } 838 } else { 839 encodeCompositeElementToStreamWriter(theResDef, theResource, theResource, theEventWriter, resDef, theContainedResource, new CompositeChildElement(resDef)); 840 } 841 842 theEventWriter.writeEnd(); 843 } 844 845 @Override 846 public void encodeTagListToWriter(TagList theTagList, Writer theWriter) throws IOException { 847 JsonGenerator eventWriter = createJsonGenerator(theWriter); 848 849 eventWriter.writeStartObject(); 850 851 eventWriter.write("resourceType", TagList.ELEMENT_NAME); 852 853 eventWriter.writeStartArray(TagList.ATTR_CATEGORY); 854 for (Tag next : theTagList) { 855 eventWriter.writeStartObject(); 856 857 if (isNotBlank(next.getTerm())) { 858 eventWriter.write(Tag.ATTR_TERM, next.getTerm()); 859 } 860 if (isNotBlank(next.getLabel())) { 861 eventWriter.write(Tag.ATTR_LABEL, next.getLabel()); 862 } 863 if (isNotBlank(next.getScheme())) { 864 eventWriter.write(Tag.ATTR_SCHEME, next.getScheme()); 865 } 866 867 eventWriter.writeEnd(); 868 } 869 eventWriter.writeEnd(); 870 871 eventWriter.writeEnd(); 872 eventWriter.flush(); 873 } 874 875 /** 876 * This is useful only for the two cases where extensions are encoded as direct children (e.g. not in some object 877 * called _name): resource extensions, and extension extensions 878 */ 879 private void extractAndWriteExtensionsAsDirectChild(IBase theElement, JsonGenerator theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef, IBaseResource theResource, String theParentExtensionUrl) throws IOException { 880 List<HeldExtension> extensions = new ArrayList<HeldExtension>(0); 881 List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0); 882 883 // Undeclared extensions 884 extractUndeclaredExtensions(theElement, extensions, modifierExtensions); 885 886 // Declared extensions 887 if (theElementDef != null) { 888 extractDeclaredExtensions(theElement, theElementDef, extensions, modifierExtensions); 889 } 890 891 // Write the extensions 892 writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, extensions, modifierExtensions, theParentExtensionUrl); 893 } 894 895 private void extractDeclaredExtensions(IBase theResource, BaseRuntimeElementDefinition<?> resDef, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions) { 896 for (RuntimeChildDeclaredExtensionDefinition nextDef : resDef.getExtensionsNonModifier()) { 897 for (IBase nextValue : nextDef.getAccessor().getValues(theResource)) { 898 if (nextValue != null) { 899 if (nextValue == null || nextValue.isEmpty()) { 900 continue; 901 } 902 extensions.add(new HeldExtension(nextDef, nextValue)); 903 } 904 } 905 } 906 for (RuntimeChildDeclaredExtensionDefinition nextDef : resDef.getExtensionsModifier()) { 907 for (IBase nextValue : nextDef.getAccessor().getValues(theResource)) { 908 if (nextValue != null) { 909 if (nextValue == null || nextValue.isEmpty()) { 910 continue; 911 } 912 modifierExtensions.add(new HeldExtension(nextDef, nextValue)); 913 } 914 } 915 } 916 } 917 918 private void extractUndeclaredExtensions(IBase theElement, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions) { 919 if (theElement instanceof ISupportsUndeclaredExtensions) { 920 ISupportsUndeclaredExtensions element = (ISupportsUndeclaredExtensions) theElement; 921 List<ExtensionDt> ext = element.getUndeclaredExtensions(); 922 for (ExtensionDt next : ext) { 923 if (next == null || next.isEmpty()) { 924 continue; 925 } 926 extensions.add(new HeldExtension(next, false)); 927 } 928 929 ext = element.getUndeclaredModifierExtensions(); 930 for (ExtensionDt next : ext) { 931 if (next == null || next.isEmpty()) { 932 continue; 933 } 934 modifierExtensions.add(new HeldExtension(next, true)); 935 } 936 } else { 937 if (theElement instanceof IBaseHasExtensions) { 938 IBaseHasExtensions element = (IBaseHasExtensions) theElement; 939 List<? extends IBaseExtension<?, ?>> ext = element.getExtension(); 940 for (IBaseExtension<?, ?> next : ext) { 941 if (next == null || (ElementUtil.isEmpty(next.getValue()) && next.getExtension().isEmpty())) { 942 continue; 943 } 944 extensions.add(new HeldExtension(next, false)); 945 } 946 } 947 if (theElement instanceof IBaseHasModifierExtensions) { 948 IBaseHasModifierExtensions element = (IBaseHasModifierExtensions) theElement; 949 List<? extends IBaseExtension<?, ?>> ext = element.getModifierExtension(); 950 for (IBaseExtension<?, ?> next : ext) { 951 if (next == null || next.isEmpty()) { 952 continue; 953 } 954 modifierExtensions.add(new HeldExtension(next, true)); 955 } 956 } 957 } 958 } 959 960 @Override 961 public EncodingEnum getEncoding() { 962 return EncodingEnum.JSON; 963 } 964 965 private void parseAlternates(JsonValue theAlternateVal, ParserState<?> theState, String theElementName) { 966 if (theAlternateVal == null || theAlternateVal.getValueType() == ValueType.NULL) { 967 return; 968 } 969 970 if (theAlternateVal instanceof JsonArray) { 971 JsonArray array = (JsonArray) theAlternateVal; 972 if (array.size() > 1) { 973 throw new DataFormatException("Unexpected array of length " + array.size() + " (expected 0 or 1) for element: " + theElementName); 974 } 975 if (array.size() == 0) { 976 return; 977 } 978 parseAlternates(array.getJsonObject(0), theState, theElementName); 979 return; 980 } 981 982 JsonObject alternate = (JsonObject) theAlternateVal; 983 for (Entry<String, JsonValue> nextEntry : alternate.entrySet()) { 984 String nextKey = nextEntry.getKey(); 985 JsonValue nextVal = nextEntry.getValue(); 986 if ("extension".equals(nextKey)) { 987 boolean isModifier = false; 988 JsonArray array = (JsonArray) nextEntry.getValue(); 989 parseExtension(theState, array, isModifier); 990 } else if ("modifierExtension".equals(nextKey)) { 991 boolean isModifier = true; 992 JsonArray array = (JsonArray) nextEntry.getValue(); 993 parseExtension(theState, array, isModifier); 994 } else if ("id".equals(nextKey)) { 995 switch (nextVal.getValueType()) { 996 case STRING: 997 theState.attributeValue("id", ((JsonString) nextVal).getString()); 998 break; 999 case NULL: 1000 break; 1001 default: 1002 break; 1003 } 1004 } 1005 } 1006 } 1007 1008 @Override 1009 public <T extends IBaseResource> Bundle parseBundle(Class<T> theResourceType, Reader theReader) { 1010 JsonReader reader; 1011 JsonObject object; 1012 1013 try { 1014 reader = Json.createReader(theReader); 1015 object = reader.readObject(); 1016 } catch (JsonParsingException e) { 1017 if (e.getMessage().startsWith("Unexpected char 39")) { 1018 throw new DataFormatException("Failed to parse JSON encoded FHIR content: " + e.getMessage() + " - This may indicate that single quotes are being used as JSON escapes where double quotes are required", e); 1019 } 1020 throw new DataFormatException("Failed to parse JSON encoded FHIR content: " + e.getMessage(), e); 1021 } 1022 JsonValue resourceTypeObj = object.get("resourceType"); 1023 assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType"); 1024 String resourceType = ((JsonString) resourceTypeObj).getString(); 1025 if (!"Bundle".equals(resourceType)) { 1026 throw new DataFormatException("Trying to parse bundle but found resourceType other than 'Bundle'. Found: '" + resourceType + "'"); 1027 } 1028 1029 ParserState<Bundle> state = ParserState.getPreAtomInstance(myContext, theResourceType, true, getErrorHandler()); 1030 if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { 1031 state.enteringNewElement(null, "Bundle"); 1032 } else { 1033 state.enteringNewElement(null, "feed"); 1034 } 1035 1036 parseBundleChildren(object, state); 1037 1038 state.endingElement(); 1039 1040 Bundle retVal = state.getObject(); 1041 1042 return retVal; 1043 } 1044 1045 private void parseBundleChildren(JsonObject theObject, ParserState<?> theState) { 1046 for (String nextName : theObject.keySet()) { 1047 if ("resourceType".equals(nextName)) { 1048 continue; 1049 } else if ("entry".equals(nextName)) { 1050 JsonArray entries = grabJsonArray(theObject, nextName, "entry"); 1051 for (JsonValue jsonValue : entries) { 1052 theState.enteringNewElement(null, "entry"); 1053 parseBundleChildren((JsonObject) jsonValue, theState); 1054 theState.endingElement(); 1055 } 1056 continue; 1057 } else if (myContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) { 1058 if ("link".equals(nextName)) { 1059 JsonArray entries = grabJsonArray(theObject, nextName, "link"); 1060 for (JsonValue jsonValue : entries) { 1061 theState.enteringNewElement(null, "link"); 1062 JsonObject linkObj = (JsonObject) jsonValue; 1063 String rel = linkObj.getString("rel", null); 1064 String href = linkObj.getString("href", null); 1065 theState.attributeValue("rel", rel); 1066 theState.attributeValue("href", href); 1067 theState.endingElement(); 1068 } 1069 continue; 1070 } else if (BUNDLE_TEXTNODE_CHILDREN_DSTU1.contains(nextName)) { 1071 theState.enteringNewElement(null, nextName); 1072 theState.string(theObject.getString(nextName, null)); 1073 theState.endingElement(); 1074 continue; 1075 } 1076 } else { 1077 if ("link".equals(nextName)) { 1078 JsonArray entries = grabJsonArray(theObject, nextName, "link"); 1079 for (JsonValue jsonValue : entries) { 1080 theState.enteringNewElement(null, "link"); 1081 JsonObject linkObj = (JsonObject) jsonValue; 1082 String rel = linkObj.getString("relation", null); 1083 String href = linkObj.getString("url", null); 1084 theState.enteringNewElement(null, "relation"); 1085 theState.attributeValue("value", rel); 1086 theState.endingElement(); 1087 theState.enteringNewElement(null, "url"); 1088 theState.attributeValue("value", href); 1089 theState.endingElement(); 1090 theState.endingElement(); 1091 } 1092 continue; 1093 } else if (BUNDLE_TEXTNODE_CHILDREN_DSTU2.contains(nextName)) { 1094 theState.enteringNewElement(null, nextName); 1095 // String obj = theObject.getString(nextName, null); 1096 1097 JsonValue obj = theObject.get(nextName); 1098 if (obj == null) { 1099 theState.attributeValue("value", null); 1100 } else if (obj instanceof JsonString) { 1101 theState.attributeValue("value", theObject.getString(nextName, null)); 1102 } else if (obj instanceof JsonNumber) { 1103 theState.attributeValue("value", obj.toString()); 1104 } else { 1105 throw new DataFormatException("Unexpected JSON object for entry '" + nextName + "'"); 1106 } 1107 1108 theState.endingElement(); 1109 continue; 1110 } 1111 } 1112 1113 JsonValue nextVal = theObject.get(nextName); 1114 parseChildren(theState, nextName, nextVal, null, null); 1115 1116 } 1117 } 1118 1119 private void parseChildren(JsonObject theObject, ParserState<?> theState) { 1120 String elementId = null; 1121 for (String nextName : theObject.keySet()) { 1122 if ("resourceType".equals(nextName)) { 1123 continue; 1124 } else if ("id".equals(nextName)) { 1125 if (theObject.isNull(nextName)) { 1126 continue; 1127 } 1128 elementId = theObject.getString(nextName); 1129 if (myContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) { 1130 continue; 1131 } 1132 } else if ("_id".equals(nextName)) { 1133 if (theObject.isNull(nextName)) { 1134 continue; 1135 } 1136 // _id is incorrect, but some early examples in the FHIR spec used it 1137 if (theObject.get(nextName).getValueType() == ValueType.STRING) { 1138 elementId = theObject.getString(nextName); 1139 } 1140 continue; 1141 } else if ("extension".equals(nextName)) { 1142 JsonArray array = grabJsonArray(theObject, nextName, "extension"); 1143 parseExtension(theState, array, false); 1144 continue; 1145 } else if ("modifierExtension".equals(nextName)) { 1146 JsonArray array = grabJsonArray(theObject, nextName, "modifierExtension"); 1147 parseExtension(theState, array, true); 1148 continue; 1149 } else if (nextName.charAt(0) == '_') { 1150 continue; 1151 } 1152 1153 JsonValue nextVal = theObject.get(nextName); 1154 String alternateName = '_' + nextName; 1155 JsonValue alternateVal = theObject.get(alternateName); 1156 1157 parseChildren(theState, nextName, nextVal, alternateVal, alternateName); 1158 1159 } 1160 1161 if (elementId != null) { 1162 IBase object = (IBase) theState.getObject(); 1163 if (object instanceof IIdentifiableElement) { 1164 ((IIdentifiableElement) object).setElementSpecificId(elementId); 1165 } else if (object instanceof IBaseResource) { 1166 ((IBaseResource) object).getIdElement().setValue(elementId); 1167 } 1168 } 1169 } 1170 1171 private JsonArray grabJsonArray(JsonObject theObject, String nextName, String thePosition) { 1172 JsonValue object = theObject.get(nextName); 1173 if (object == null) { 1174 return null; 1175 } 1176 if (object.getValueType() != ValueType.ARRAY) { 1177 throw new DataFormatException("Syntax error parsing JSON FHIR structure: Expected ARRAY at element '" + thePosition + "', found '" + object.getValueType().name() + "'"); 1178 } 1179 return (JsonArray) object; 1180 } 1181 1182 private void parseChildren(ParserState<?> theState, String theName, JsonValue theJsonVal, JsonValue theAlternateVal, String theAlternateName) { 1183 switch (theJsonVal.getValueType()) { 1184 case ARRAY: { 1185 JsonArray nextArray = (JsonArray) theJsonVal; 1186 JsonArray nextAlternateArray = (JsonArray) theAlternateVal; 1187 for (int i = 0; i < nextArray.size(); i++) { 1188 JsonValue nextObject = nextArray.get(i); 1189 JsonValue nextAlternate = null; 1190 if (nextAlternateArray != null) { 1191 nextAlternate = nextAlternateArray.get(i); 1192 } 1193 parseChildren(theState, theName, nextObject, nextAlternate, theAlternateName); 1194 } 1195 break; 1196 } 1197 case OBJECT: { 1198 theState.enteringNewElement(null, theName); 1199 parseAlternates(theAlternateVal, theState, theAlternateName); 1200 JsonObject nextObject = (JsonObject) theJsonVal; 1201 boolean preResource = false; 1202 if (theState.isPreResource()) { 1203 String resType = nextObject.getString("resourceType", null); 1204 if (isBlank(resType)) { 1205 throw new DataFormatException("Missing required element 'resourceType' from JSON resource object, unable to parse"); 1206 } 1207 theState.enteringNewElement(null, resType); 1208 preResource = true; 1209 } 1210 parseChildren(nextObject, theState); 1211 if (preResource) { 1212 theState.endingElement(); 1213 } 1214 theState.endingElement(); 1215 break; 1216 } 1217 case STRING: { 1218 JsonString nextValStr = (JsonString) theJsonVal; 1219 theState.enteringNewElement(null, theName); 1220 theState.attributeValue("value", nextValStr.getString()); 1221 parseAlternates(theAlternateVal, theState, theAlternateName); 1222 theState.endingElement(); 1223 break; 1224 } 1225 case NUMBER: 1226 JsonNumber nextValNumber = (JsonNumber) theJsonVal; 1227 theState.enteringNewElement(null, theName); 1228 theState.attributeValue("value", nextValNumber.bigDecimalValue().toPlainString()); 1229 parseAlternates(theAlternateVal, theState, theAlternateName); 1230 theState.endingElement(); 1231 break; 1232 case FALSE: 1233 case TRUE: 1234 theState.enteringNewElement(null, theName); 1235 theState.attributeValue("value", theJsonVal.toString()); 1236 parseAlternates(theAlternateVal, theState, theAlternateName); 1237 theState.endingElement(); 1238 break; 1239 case NULL: 1240 break; 1241 } 1242 } 1243 1244 private void parseExtension(ParserState<?> theState, JsonArray theValues, boolean theIsModifier) { 1245 for (int i = 0; i < theValues.size(); i++) { 1246 JsonObject nextExtObj = theValues.getJsonObject(i); 1247 String url = nextExtObj.getString("url"); 1248 theState.enteringNewElementExtension(null, url, theIsModifier); 1249 for (Iterator<String> iter = nextExtObj.keySet().iterator(); iter.hasNext();) { 1250 String next = iter.next(); 1251 if ("url".equals(next)) { 1252 continue; 1253 } else if ("extension".equals(next)) { 1254 JsonArray jsonVal = (JsonArray) nextExtObj.get(next); 1255 parseExtension(theState, jsonVal, false); 1256 } else if ("modifierExtension".equals(next)) { 1257 JsonArray jsonVal = (JsonArray) nextExtObj.get(next); 1258 parseExtension(theState, jsonVal, true); 1259 } else { 1260 JsonValue jsonVal = nextExtObj.get(next); 1261 parseChildren(theState, next, jsonVal, null, null); 1262 } 1263 } 1264 theState.endingElement(); 1265 } 1266 } 1267 1268 // private void parseExtensionInDstu2Style(boolean theModifier, ParserState<?> theState, String 1269 // theParentExtensionUrl, String theExtensionUrl, JsonArray theValues) { 1270 // String extUrl = UrlUtil.constructAbsoluteUrl(theParentExtensionUrl, theExtensionUrl); 1271 // theState.enteringNewElementExtension(null, extUrl, theModifier); 1272 // 1273 // for (int extIdx = 0; extIdx < theValues.size(); extIdx++) { 1274 // JsonObject nextExt = theValues.getJsonObject(extIdx); 1275 // for (String nextKey : nextExt.keySet()) { 1276 // // if (nextKey.startsWith("value") && nextKey.length() > 5 && 1277 // // myContext.getRuntimeChildUndeclaredExtensionDefinition().getChildByName(nextKey) != null) { 1278 // JsonValue jsonVal = nextExt.get(nextKey); 1279 // if (jsonVal.getValueType() == ValueType.ARRAY) { 1280 // /* 1281 // * Extension children which are arrays are sub-extensions. Any other value type should be treated as a value. 1282 // */ 1283 // JsonArray arrayValue = (JsonArray) jsonVal; 1284 // parseExtensionInDstu2Style(theModifier, theState, extUrl, nextKey, arrayValue); 1285 // } else { 1286 // parseChildren(theState, nextKey, jsonVal, null, null); 1287 // } 1288 // } 1289 // } 1290 // 1291 // theState.endingElement(); 1292 // } 1293 1294 @Override 1295 public TagList parseTagList(Reader theReader) { 1296 JsonReader reader = Json.createReader(theReader); 1297 JsonObject object = reader.readObject(); 1298 1299 JsonValue resourceTypeObj = object.get("resourceType"); 1300 assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType"); 1301 String resourceType = ((JsonString) resourceTypeObj).getString(); 1302 1303 ParserState<TagList> state = ParserState.getPreTagListInstance(myContext, true, getErrorHandler()); 1304 state.enteringNewElement(null, resourceType); 1305 1306 parseChildren(object, state); 1307 1308 state.endingElement(); 1309 1310 return state.getObject(); 1311 } 1312 1313 @Override 1314 public IParser setPrettyPrint(boolean thePrettyPrint) { 1315 myPrettyPrint = thePrettyPrint; 1316 return this; 1317 } 1318 1319 private boolean writeAtomLinkInDstu1Format(JsonGenerator theEventWriter, String theRel, StringDt theLink, boolean theStarted) { 1320 boolean retVal = theStarted; 1321 if (isNotBlank(theLink.getValue())) { 1322 if (theStarted == false) { 1323 theEventWriter.writeStartArray("link"); 1324 retVal = true; 1325 } 1326 1327 theEventWriter.writeStartObject(); 1328 theEventWriter.write("rel", theRel); 1329 theEventWriter.write("href", theLink.getValue()); 1330 theEventWriter.writeEnd(); 1331 } 1332 return retVal; 1333 } 1334 1335 private boolean writeAtomLinkInDstu2Format(JsonGenerator theEventWriter, String theRel, StringDt theLink, boolean theStarted) { 1336 boolean retVal = theStarted; 1337 if (isNotBlank(theLink.getValue())) { 1338 if (theStarted == false) { 1339 theEventWriter.writeStartArray("link"); 1340 retVal = true; 1341 } 1342 1343 theEventWriter.writeStartObject(); 1344 theEventWriter.write("relation", theRel); 1345 theEventWriter.write("url", theLink.getValue()); 1346 theEventWriter.writeEnd(); 1347 } 1348 return retVal; 1349 } 1350 1351 private void writeAuthor(BaseBundle theBundle, JsonGenerator eventWriter) { 1352 if (StringUtils.isNotBlank(theBundle.getAuthorName().getValue())) { 1353 eventWriter.writeStartArray("author"); 1354 eventWriter.writeStartObject(); 1355 writeTagWithTextNode(eventWriter, "name", theBundle.getAuthorName()); 1356 writeOptionalTagWithTextNode(eventWriter, "uri", theBundle.getAuthorUri()); 1357 eventWriter.writeEnd(); 1358 eventWriter.writeEnd(); 1359 } 1360 } 1361 1362 private void writeCategories(JsonGenerator eventWriter, TagList categories) { 1363 if (categories != null && categories.size() > 0) { 1364 eventWriter.writeStartArray("category"); 1365 for (Tag next : categories) { 1366 eventWriter.writeStartObject(); 1367 eventWriter.write("term", defaultString(next.getTerm())); 1368 eventWriter.write("label", defaultString(next.getLabel())); 1369 eventWriter.write("scheme", defaultString(next.getScheme())); 1370 eventWriter.writeEnd(); 1371 } 1372 eventWriter.writeEnd(); 1373 } 1374 } 1375 1376 private void writeExtensionsAsDirectChild(IBaseResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions, String theParentExtensionUrl) throws IOException { 1377 if (extensions.isEmpty() == false) { 1378 theEventWriter.writeStartArray("extension"); 1379 for (HeldExtension next : extensions) { 1380 next.write(resDef, theResource, theEventWriter); 1381 } 1382 theEventWriter.writeEnd(); 1383 } 1384 if (modifierExtensions.isEmpty() == false) { 1385 theEventWriter.writeStartArray("modifierExtension"); 1386 for (HeldExtension next : modifierExtensions) { 1387 next.write(resDef, theResource, theEventWriter); 1388 } 1389 theEventWriter.writeEnd(); 1390 } 1391 } 1392 1393 private void writeOptionalTagWithDecimalNode(JsonGenerator theEventWriter, String theElementName, DecimalDt theValue) { 1394 if (theValue != null && theValue.isEmpty() == false) { 1395 theEventWriter.write(theElementName, theValue.getValue()); 1396 } 1397 } 1398 1399 private void writeOptionalTagWithNumberNode(JsonGenerator theEventWriter, String theElementName, IntegerDt theValue) { 1400 if (theValue != null && theValue.isEmpty() == false) { 1401 theEventWriter.write(theElementName, theValue.getValue().intValue()); 1402 } 1403 } 1404 1405 private void writeOptionalTagWithTextNode(JsonGenerator theEventWriter, String theElementName, IPrimitiveDatatype<?> thePrimitive) { 1406 if (thePrimitive == null) { 1407 return; 1408 } 1409 String str = thePrimitive.getValueAsString(); 1410 writeOptionalTagWithTextNode(theEventWriter, theElementName, str); 1411 } 1412 1413 private void writeOptionalTagWithTextNode(JsonGenerator theEventWriter, String theElementName, String theValue) { 1414 if (StringUtils.isNotBlank(theValue)) { 1415 theEventWriter.write(theElementName, theValue); 1416 } 1417 } 1418 1419 private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, IPrimitiveDatatype<?> theIdDt) { 1420 if (theIdDt != null && !theIdDt.isEmpty()) { 1421 theEventWriter.write(theElementName, theIdDt.getValueAsString()); 1422 } else { 1423 theEventWriter.writeNull(theElementName); 1424 } 1425 } 1426 1427 private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, StringDt theStringDt) { 1428 if (StringUtils.isNotBlank(theStringDt.getValue())) { 1429 theEventWriter.write(theElementName, theStringDt.getValue()); 1430 } 1431 // else { 1432 // theEventWriter.writeNull(theElementName); 1433 // } 1434 } 1435 1436 private class HeldExtension implements Comparable<HeldExtension> { 1437 1438 private RuntimeChildDeclaredExtensionDefinition myDef; 1439 private boolean myModifier; 1440 private IBaseExtension<?, ?> myUndeclaredExtension; 1441 private IBase myValue; 1442 1443 public HeldExtension(IBaseExtension<?, ?> theUndeclaredExtension, boolean theModifier) { 1444 assert theUndeclaredExtension != null; 1445 myUndeclaredExtension = theUndeclaredExtension; 1446 myModifier = theModifier; 1447 } 1448 1449 public HeldExtension(RuntimeChildDeclaredExtensionDefinition theDef, IBase theValue) { 1450 assert theDef != null; 1451 assert theValue != null; 1452 myDef = theDef; 1453 myValue = theValue; 1454 } 1455 1456 @Override 1457 public int compareTo(HeldExtension theArg0) { 1458 String url1 = myDef != null ? myDef.getExtensionUrl() : myUndeclaredExtension.getUrl(); 1459 String url2 = theArg0.myDef != null ? theArg0.myDef.getExtensionUrl() : theArg0.myUndeclaredExtension.getUrl(); 1460 url1 = defaultString(url1); 1461 url2 = defaultString(url2); 1462 return url1.compareTo(url2); 1463 } 1464 1465 public void write(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter) throws IOException { 1466 if (myUndeclaredExtension != null) { 1467 writeUndeclaredExtInDstu1Format(theResDef, theResource, theEventWriter, myUndeclaredExtension); 1468 } else { 1469 theEventWriter.writeStartObject(); 1470 theEventWriter.write("url", myDef.getExtensionUrl()); 1471 1472 BaseRuntimeElementDefinition<?> def = myDef.getChildElementDefinitionByDatatype(myValue.getClass()); 1473 if (def.getChildType() == ChildTypeEnum.RESOURCE_BLOCK) { 1474 extractAndWriteExtensionsAsDirectChild(myValue, theEventWriter, def, theResDef, theResource, myDef.getExtensionUrl()); 1475 } else { 1476 String childName = myDef.getChildNameByDatatype(myValue.getClass()); 1477 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName, false, null); 1478 } 1479 1480 theEventWriter.writeEnd(); 1481 } 1482 } 1483 1484 private void writeUndeclaredExtInDstu1Format(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, IBaseExtension<?, ?> ext) throws IOException { 1485 IBaseDatatype value = ext.getValue(); 1486 String extensionUrl = ext.getUrl(); 1487 1488 theEventWriter.writeStartObject(); 1489 theEventWriter.write("url", extensionUrl); 1490 1491 boolean noValue = value == null || value.isEmpty(); 1492 if (noValue && ext.getExtension().isEmpty()) { 1493 ourLog.debug("Extension with URL[{}] has no value", extensionUrl); 1494 } else if (noValue) { 1495 1496 if (myModifier) { 1497 theEventWriter.writeStartArray("modifierExtension"); 1498 } else { 1499 theEventWriter.writeStartArray("extension"); 1500 } 1501 1502 for (Object next : ext.getExtension()) { 1503 writeUndeclaredExtInDstu1Format(theResDef, theResource, theEventWriter, (IBaseExtension<?, ?>) next); 1504 } 1505 theEventWriter.writeEnd(); 1506 } else { 1507 RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition(); 1508 String childName = extDef.getChildNameByDatatype(value.getClass()); 1509 if (childName == null) { 1510 childName = "value" + myContext.getElementDefinition(value.getClass()).getName(); 1511 } 1512 BaseRuntimeElementDefinition<?> childDef = myContext.getElementDefinition(value.getClass()); 1513 if (childDef == null) { 1514 throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + value.getClass().getCanonicalName()); 1515 } 1516 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName, true, null); 1517 } 1518 1519 // theEventWriter.name(myUndeclaredExtension.get); 1520 1521 theEventWriter.writeEnd(); 1522 } 1523 1524 } 1525}