001package ca.uhn.fhir.parser; 002 003/* 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2017 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.HashSet; 034import java.util.List; 035import java.util.Set; 036 037import org.apache.commons.lang3.StringUtils; 038import org.apache.commons.lang3.Validate; 039import org.apache.commons.lang3.text.WordUtils; 040import org.hl7.fhir.instance.model.api.IBase; 041import org.hl7.fhir.instance.model.api.IBaseBinary; 042import org.hl7.fhir.instance.model.api.IBaseBooleanDatatype; 043import org.hl7.fhir.instance.model.api.IBaseDecimalDatatype; 044import org.hl7.fhir.instance.model.api.IBaseExtension; 045import org.hl7.fhir.instance.model.api.IBaseHasExtensions; 046import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions; 047import org.hl7.fhir.instance.model.api.IBaseIntegerDatatype; 048import org.hl7.fhir.instance.model.api.IBaseResource; 049import org.hl7.fhir.instance.model.api.IDomainResource; 050import org.hl7.fhir.instance.model.api.IIdType; 051import org.hl7.fhir.instance.model.api.INarrative; 052import org.hl7.fhir.instance.model.api.IPrimitiveType; 053 054import com.google.gson.Gson; 055import com.google.gson.GsonBuilder; 056 057import ca.uhn.fhir.context.BaseRuntimeChildDefinition; 058import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; 059import ca.uhn.fhir.context.BaseRuntimeElementDefinition; 060import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum; 061import ca.uhn.fhir.context.ConfigurationException; 062import ca.uhn.fhir.context.FhirContext; 063import ca.uhn.fhir.context.FhirVersionEnum; 064import ca.uhn.fhir.context.RuntimeChildContainedResources; 065import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition; 066import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition; 067import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition; 068import ca.uhn.fhir.context.RuntimeResourceDefinition; 069import ca.uhn.fhir.model.api.BaseBundle; 070import ca.uhn.fhir.model.api.Bundle; 071import ca.uhn.fhir.model.api.BundleEntry; 072import ca.uhn.fhir.model.api.ExtensionDt; 073import ca.uhn.fhir.model.api.IPrimitiveDatatype; 074import ca.uhn.fhir.model.api.IResource; 075import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions; 076import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; 077import ca.uhn.fhir.model.api.Tag; 078import ca.uhn.fhir.model.api.TagList; 079import ca.uhn.fhir.model.api.annotation.Child; 080import ca.uhn.fhir.model.base.composite.BaseCodingDt; 081import ca.uhn.fhir.model.base.composite.BaseContainedDt; 082import ca.uhn.fhir.model.primitive.DecimalDt; 083import ca.uhn.fhir.model.primitive.IdDt; 084import ca.uhn.fhir.model.primitive.InstantDt; 085import ca.uhn.fhir.model.primitive.IntegerDt; 086import ca.uhn.fhir.model.primitive.StringDt; 087import ca.uhn.fhir.narrative.INarrativeGenerator; 088import ca.uhn.fhir.parser.json.GsonStructure; 089import ca.uhn.fhir.parser.json.JsonLikeArray; 090import ca.uhn.fhir.parser.json.JsonLikeObject; 091import ca.uhn.fhir.parser.json.JsonLikeStructure; 092import ca.uhn.fhir.parser.json.JsonLikeValue; 093import ca.uhn.fhir.parser.json.JsonLikeValue.ScalarType; 094import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType; 095import ca.uhn.fhir.parser.json.JsonLikeWriter; 096import ca.uhn.fhir.rest.server.EncodingEnum; 097import ca.uhn.fhir.util.ElementUtil; 098 099/** 100 * This class is the FHIR JSON parser/encoder. Users should not interact with this class directly, but should use 101 * {@link FhirContext#newJsonParser()} to get an instance. 102 */ 103public class JsonParser extends BaseParser implements IJsonLikeParser { 104 105 private static final Set<String> BUNDLE_TEXTNODE_CHILDREN_DSTU1; 106 private static final Set<String> BUNDLE_TEXTNODE_CHILDREN_DSTU2; 107 private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParser.HeldExtension.class); 108 109 static { 110 HashSet<String> hashSetDstu1 = new HashSet<String>(); 111 hashSetDstu1.add("title"); 112 hashSetDstu1.add("id"); 113 hashSetDstu1.add("updated"); 114 hashSetDstu1.add("published"); 115 hashSetDstu1.add("totalResults"); 116 BUNDLE_TEXTNODE_CHILDREN_DSTU1 = Collections.unmodifiableSet(hashSetDstu1); 117 118 HashSet<String> hashSetDstu2 = new HashSet<String>(); 119 hashSetDstu2.add("type"); 120 hashSetDstu2.add("base"); 121 hashSetDstu2.add("total"); 122 BUNDLE_TEXTNODE_CHILDREN_DSTU2 = Collections.unmodifiableSet(hashSetDstu2); 123 } 124 125 private FhirContext myContext; 126 private boolean myPrettyPrint; 127 128 /** 129 * Do not use this constructor, the recommended way to obtain a new instance of the JSON parser is to invoke 130 * {@link FhirContext#newJsonParser()}. 131 * 132 * @param theParserErrorHandler 133 */ 134 public JsonParser(FhirContext theContext, IParserErrorHandler theParserErrorHandler) { 135 super(theContext, theParserErrorHandler); 136 myContext = theContext; 137 } 138 139 private boolean addToHeldComments(int valueIdx, List<String> theCommentsToAdd, ArrayList<ArrayList<String>> theListToAddTo) { 140 if (theCommentsToAdd.size() > 0) { 141 theListToAddTo.ensureCapacity(valueIdx); 142 while (theListToAddTo.size() <= valueIdx) { 143 theListToAddTo.add(null); 144 } 145 if (theListToAddTo.get(valueIdx) == null) { 146 theListToAddTo.set(valueIdx, new ArrayList<String>()); 147 } 148 theListToAddTo.get(valueIdx).addAll(theCommentsToAdd); 149 return true; 150 } 151 return false; 152 } 153 154 private boolean addToHeldExtensions(int valueIdx, List<? extends IBaseExtension<?, ?>> ext, ArrayList<ArrayList<HeldExtension>> list, boolean theIsModifier, CompositeChildElement theChildElem, CompositeChildElement theParent) { 155 if (ext.size() > 0) { 156 list.ensureCapacity(valueIdx); 157 while (list.size() <= valueIdx) { 158 list.add(null); 159 } 160 if (list.get(valueIdx) == null) { 161 list.set(valueIdx, new ArrayList<JsonParser.HeldExtension>()); 162 } 163 for (IBaseExtension<?, ?> next : ext) { 164 list.get(valueIdx).add(new HeldExtension(next, theIsModifier, theChildElem, theParent)); 165 } 166 return true; 167 } 168 return false; 169 } 170 171 private void addToHeldIds(int theValueIdx, ArrayList<String> theListToAddTo, String theId) { 172 theListToAddTo.ensureCapacity(theValueIdx); 173 while (theListToAddTo.size() <= theValueIdx) { 174 theListToAddTo.add(null); 175 } 176 if (theListToAddTo.get(theValueIdx) == null) { 177 theListToAddTo.set(theValueIdx, theId); 178 } 179 } 180 181// private void assertObjectOfType(JsonLikeValue theResourceTypeObj, Object theValueType, String thePosition) { 182// if (theResourceTypeObj == null) { 183// throw new DataFormatException("Invalid JSON content detected, missing required element: '" + thePosition + "'"); 184// } 185// 186// if (theResourceTypeObj.getValueType() != theValueType) { 187// throw new DataFormatException("Invalid content of element " + thePosition + ", expected " + theValueType); 188// } 189// } 190 191 private void beginArray(JsonLikeWriter theEventWriter, String arrayName) throws IOException { 192 theEventWriter.beginArray(arrayName); 193 } 194 195 private void beginObject(JsonLikeWriter theEventWriter, String arrayName) throws IOException { 196 theEventWriter.beginObject(arrayName); 197 } 198 199 private JsonLikeWriter createJsonWriter(Writer theWriter) { 200 JsonLikeStructure jsonStructure = new GsonStructure(); 201 JsonLikeWriter retVal = jsonStructure.getJsonLikeWriter(theWriter); 202 return retVal; 203 } 204 205 @Override 206 public void doEncodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException { 207 JsonLikeWriter eventWriter = createJsonWriter(theWriter); 208 doEncodeBundleToJsonLikeWriter(theBundle, eventWriter); 209 } 210 211 public void doEncodeBundleToJsonLikeWriter(Bundle theBundle, JsonLikeWriter theEventWriter) throws IOException { 212 if (myPrettyPrint) { 213 theEventWriter.setPrettyPrint(myPrettyPrint); 214 } 215 theEventWriter.init(); 216 217 if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { 218 encodeBundleToWriterInDstu2Format(theBundle, theEventWriter); 219 } else { 220 encodeBundleToWriterInDstu1Format(theBundle, theEventWriter); 221 } 222 theEventWriter.flush(); 223 } 224 225 @Override 226 protected void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws IOException { 227 JsonLikeWriter eventWriter = createJsonWriter(theWriter); 228 doEncodeResourceToJsonLikeWriter(theResource, eventWriter); 229 } 230 231 public void doEncodeResourceToJsonLikeWriter(IBaseResource theResource, JsonLikeWriter theEventWriter) throws IOException { 232 if (myPrettyPrint) { 233 theEventWriter.setPrettyPrint(myPrettyPrint); 234 } 235 theEventWriter.init(); 236 237 RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource); 238 encodeResourceToJsonStreamWriter(resDef, theResource, theEventWriter, null, false, false); 239 theEventWriter.flush(); 240 } 241 242 @Override 243 public <T extends IBaseResource> T doParseResource(Class<T> theResourceType, Reader theReader) { 244 JsonLikeStructure jsonStructure = new GsonStructure(); 245 jsonStructure.load(theReader); 246 247 T retVal = doParseResource(theResourceType, jsonStructure); 248 249 return retVal; 250 } 251 252 public <T extends IBaseResource> T doParseResource(Class<T> theResourceType, JsonLikeStructure theJsonStructure) { 253 JsonLikeObject object = theJsonStructure.getRootObject(); 254 255 JsonLikeValue resourceTypeObj = object.get("resourceType"); 256 if (resourceTypeObj == null || !resourceTypeObj.isString() || isBlank(resourceTypeObj.getAsString())) { 257 throw new DataFormatException("Invalid JSON content detected, missing required element: 'resourceType'"); 258 } 259 260 String resourceType = resourceTypeObj.getAsString(); 261 262 ParserState<? extends IBaseResource> state = ParserState.getPreResourceInstance(this, theResourceType, myContext, true, getErrorHandler()); 263 state.enteringNewElement(null, resourceType); 264 265 parseChildren(object, state); 266 267 state.endingElement(); 268 state.endingElement(); 269 270 @SuppressWarnings("unchecked") 271 T retVal = (T) state.getObject(); 272 273 return retVal; 274 } 275 276 @Override 277 public void encodeBundleToJsonLikeWriter(Bundle theBundle, JsonLikeWriter theJsonLikeWriter) throws IOException, DataFormatException { 278 Validate.notNull(theBundle, "theBundle must not be null"); 279 Validate.notNull(theJsonLikeWriter, "theJsonLikeWriter must not be null"); 280 281 doEncodeBundleToJsonLikeWriter(theBundle, theJsonLikeWriter); 282 } 283 284 private void encodeBundleToWriterInDstu1Format(Bundle theBundle, JsonLikeWriter theEventWriter) throws IOException { 285 theEventWriter.beginObject(); 286 287 write(theEventWriter, "resourceType", "Bundle"); 288 289 writeTagWithTextNode(theEventWriter, "title", theBundle.getTitle()); 290 writeTagWithTextNode(theEventWriter, "id", theBundle.getBundleId()); 291 writeOptionalTagWithTextNode(theEventWriter, "updated", theBundle.getUpdated()); 292 293 boolean linkStarted = false; 294 linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "self", theBundle.getLinkSelf(), linkStarted); 295 linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "first", theBundle.getLinkFirst(), linkStarted); 296 linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "previous", theBundle.getLinkPrevious(), linkStarted); 297 linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "next", theBundle.getLinkNext(), linkStarted); 298 linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "last", theBundle.getLinkLast(), linkStarted); 299 linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "fhir-base", theBundle.getLinkBase(), linkStarted); 300 if (linkStarted) { 301 theEventWriter.endArray(); 302 } 303 304 writeCategories(theEventWriter, theBundle.getCategories()); 305 306 writeOptionalTagWithTextNode(theEventWriter, "totalResults", theBundle.getTotalResults()); 307 308 writeAuthor(theBundle, theEventWriter); 309 310 beginArray(theEventWriter, "entry"); 311 for (BundleEntry nextEntry : theBundle.getEntries()) { 312 theEventWriter.beginObject(); 313 314 boolean deleted = nextEntry.getDeletedAt() != null && nextEntry.getDeletedAt().isEmpty() == false; 315 if (deleted) { 316 writeTagWithTextNode(theEventWriter, "deleted", nextEntry.getDeletedAt()); 317 } 318 writeTagWithTextNode(theEventWriter, "title", nextEntry.getTitle()); 319 //TODO: Use of a deprecated method should be resolved. 320 writeTagWithTextNode(theEventWriter, "id", nextEntry.getId()); 321 322 linkStarted = false; 323 linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "self", nextEntry.getLinkSelf(), linkStarted); 324 linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "alternate", nextEntry.getLinkAlternate(), linkStarted); 325 linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "search", nextEntry.getLinkSearch(), linkStarted); 326 if (linkStarted) { 327 theEventWriter.endArray(); 328 } 329 330 //TODO: Use of a deprecated method should be resolved. 331 writeOptionalTagWithTextNode(theEventWriter, "updated", nextEntry.getUpdated()); 332 writeOptionalTagWithTextNode(theEventWriter, "published", nextEntry.getPublished()); 333 334 writeCategories(theEventWriter, nextEntry.getCategories()); 335 336 writeAuthor(nextEntry, theEventWriter); 337 338 IResource resource = nextEntry.getResource(); 339 if (resource != null && !resource.isEmpty() && !deleted) { 340 RuntimeResourceDefinition resDef = myContext.getResourceDefinition(resource); 341 encodeResourceToJsonStreamWriter(resDef, resource, theEventWriter, "content", false, true); 342 } 343 344 if (nextEntry.getSummary().isEmpty() == false) { 345 write(theEventWriter, "summary", nextEntry.getSummary().getValueAsString()); 346 } 347 348 theEventWriter.endObject(); // entry object 349 } 350 theEventWriter.endArray(); // entry array 351 352 theEventWriter.endObject(); // resource object 353 } 354 355 private void encodeBundleToWriterInDstu2Format(Bundle theBundle, JsonLikeWriter theEventWriter) throws IOException { 356 theEventWriter.beginObject(); 357 358 write(theEventWriter, "resourceType", "Bundle"); 359 360 writeOptionalTagWithTextNode(theEventWriter, "id", theBundle.getId().getIdPart()); 361 362 if (!ElementUtil.isEmpty(theBundle.getId().getVersionIdPart(), theBundle.getUpdated())) { 363 beginObject(theEventWriter, "meta"); 364 writeOptionalTagWithTextNode(theEventWriter, "versionId", theBundle.getId().getVersionIdPart()); 365 writeOptionalTagWithTextNode(theEventWriter, "lastUpdated", theBundle.getUpdated()); 366 theEventWriter.endObject(); 367 } 368 369 writeOptionalTagWithTextNode(theEventWriter, "type", theBundle.getType()); 370 371 writeOptionalTagWithNumberNode(theEventWriter, "total", theBundle.getTotalResults()); 372 373 boolean linkStarted = false; 374 linkStarted = writeAtomLinkInDstu2Format(theEventWriter, "next", theBundle.getLinkNext(), linkStarted); 375 linkStarted = writeAtomLinkInDstu2Format(theEventWriter, "self", theBundle.getLinkSelf(), linkStarted); 376 linkStarted = writeAtomLinkInDstu2Format(theEventWriter, "first", theBundle.getLinkFirst(), linkStarted); 377 linkStarted = writeAtomLinkInDstu2Format(theEventWriter, "previous", theBundle.getLinkPrevious(), linkStarted); 378 linkStarted = writeAtomLinkInDstu2Format(theEventWriter, "last", theBundle.getLinkLast(), linkStarted); 379 if (linkStarted) { 380 theEventWriter.endArray(); 381 } 382 383 beginArray(theEventWriter, "entry"); 384 for (BundleEntry nextEntry : theBundle.getEntries()) { 385 theEventWriter.beginObject(); 386 387 if (nextEntry.getResource() != null && isNotBlank(nextEntry.getResource().getIdElement().getValue()) && (nextEntry.getResource().getId().getBaseUrl() != null || nextEntry.getResource().getId().getValueAsString().startsWith("urn:"))) { 388 writeOptionalTagWithTextNode(theEventWriter, "fullUrl", nextEntry.getResource().getId().getValue()); 389 } 390 391 boolean deleted = nextEntry.getDeletedAt() != null && nextEntry.getDeletedAt().isEmpty() == false; 392 IResource resource = nextEntry.getResource(); 393 if (resource != null && !resource.isEmpty() && !deleted) { 394 RuntimeResourceDefinition resDef = myContext.getResourceDefinition(resource); 395 encodeResourceToJsonStreamWriter(resDef, resource, theEventWriter, "resource", false, true); 396 } 397 398 if (nextEntry.getSearchMode().isEmpty() == false || nextEntry.getScore().isEmpty() == false) { 399 beginObject(theEventWriter, "search"); 400 writeOptionalTagWithTextNode(theEventWriter, "mode", nextEntry.getSearchMode().getValueAsString()); 401 writeOptionalTagWithDecimalNode(theEventWriter, "score", nextEntry.getScore()); 402 theEventWriter.endObject(); 403 // IResource nextResource = nextEntry.getResource(); 404 } 405 406 if (nextEntry.getTransactionMethod().isEmpty() == false || nextEntry.getLinkSearch().isEmpty() == false) { 407 beginObject(theEventWriter, "request"); 408 writeOptionalTagWithTextNode(theEventWriter, "method", nextEntry.getTransactionMethod().getValue()); 409 writeOptionalTagWithTextNode(theEventWriter, "url", nextEntry.getLinkSearch().getValue()); 410 theEventWriter.endObject(); 411 } 412 413 if (deleted) { 414 beginObject(theEventWriter, "deleted"); 415 if (nextEntry.getResource() != null) { 416 write(theEventWriter, "type", myContext.getResourceDefinition(nextEntry.getResource()).getName()); 417 writeOptionalTagWithTextNode(theEventWriter, "resourceId", nextEntry.getResource().getId().getIdPart()); 418 writeOptionalTagWithTextNode(theEventWriter, "versionId", nextEntry.getResource().getId().getVersionIdPart()); 419 } 420 writeTagWithTextNode(theEventWriter, "instant", nextEntry.getDeletedAt()); 421 theEventWriter.endObject(); 422 } 423 424 // linkStarted = false; 425 // linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "self", nextEntry.getLinkSelf(), linkStarted); 426 // linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "alternate", nextEntry.getLinkAlternate(), 427 // linkStarted); 428 // linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "search", nextEntry.getLinkSearch(), 429 // linkStarted); 430 // if (linkStarted) { 431 // theEventWriter.writeEnd(); 432 // } 433 // 434 // writeOptionalTagWithTextNode(theEventWriter, "updated", nextEntry.getUpdated()); 435 // writeOptionalTagWithTextNode(theEventWriter, "published", nextEntry.getPublished()); 436 // 437 // writeCategories(theEventWriter, nextEntry.getCategories()); 438 // 439 // writeAuthor(nextEntry, theEventWriter); 440 441 if (nextEntry.getSummary().isEmpty() == false) { 442 write(theEventWriter, "summary", nextEntry.getSummary().getValueAsString()); 443 } 444 445 theEventWriter.endObject(); // entry object 446 } 447 theEventWriter.endArray(); // entry array 448 449 theEventWriter.endObject(); // resource object 450 } 451 452 private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonLikeWriter theEventWriter, IBase theNextValue, BaseRuntimeElementDefinition<?> theChildDef, String theChildName, boolean theContainedResource, CompositeChildElement theChildElem, 453 boolean theForceEmpty) throws IOException { 454 455 switch (theChildDef.getChildType()) { 456 case ID_DATATYPE: { 457 IIdType value = (IIdType) theNextValue; 458 String encodedValue = "id".equals(theChildName) ? value.getIdPart() : value.getValue(); 459 if (isBlank(encodedValue)) { 460 break; 461 } 462 if (theChildName != null) { 463 write(theEventWriter, theChildName, encodedValue); 464 } else { 465 theEventWriter.write(encodedValue); 466 } 467 break; 468 } 469 case PRIMITIVE_DATATYPE: { 470 final IPrimitiveType<?> value = (IPrimitiveType<?>) theNextValue; 471 if (isBlank(value.getValueAsString())) { 472 if (theForceEmpty) { 473 theEventWriter.writeNull(); 474 } 475 break; 476 } 477 478 if (value instanceof IBaseIntegerDatatype) { 479 if (theChildName != null) { 480 write(theEventWriter, theChildName, ((IBaseIntegerDatatype) value).getValue()); 481 } else { 482 theEventWriter.write(((IBaseIntegerDatatype) value).getValue()); 483 } 484 } else if (value instanceof IBaseDecimalDatatype) { 485 BigDecimal decimalValue = ((IBaseDecimalDatatype) value).getValue(); 486 decimalValue = new BigDecimal(decimalValue.toString()) { 487 private static final long serialVersionUID = 1L; 488 489 @Override 490 public String toString() { 491 return value.getValueAsString(); 492 } 493 }; 494 if (theChildName != null) { 495 write(theEventWriter, theChildName, decimalValue); 496 } else { 497 theEventWriter.write(decimalValue); 498 } 499 } else if (value instanceof IBaseBooleanDatatype) { 500 if (theChildName != null) { 501 write(theEventWriter, theChildName, ((IBaseBooleanDatatype) value).getValue()); 502 } else { 503 Boolean booleanValue = ((IBaseBooleanDatatype) value).getValue(); 504 if (booleanValue != null) { 505 theEventWriter.write(booleanValue.booleanValue()); 506 } 507 } 508 } else { 509 String valueStr = value.getValueAsString(); 510 if (theChildName != null) { 511 write(theEventWriter, theChildName, valueStr); 512 } else { 513 theEventWriter.write(valueStr); 514 } 515 } 516 break; 517 } 518 case RESOURCE_BLOCK: 519 case COMPOSITE_DATATYPE: { 520 if (theChildName != null) { 521 theEventWriter.beginObject(theChildName); 522 } else { 523 theEventWriter.beginObject(); 524 } 525 encodeCompositeElementToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, theContainedResource, theChildElem); 526 theEventWriter.endObject(); 527 break; 528 } 529 case CONTAINED_RESOURCE_LIST: 530 case CONTAINED_RESOURCES: { 531 /* 532 * Disabled per #103 ContainedDt value = (ContainedDt) theNextValue; for (IResource next : 533 * value.getContainedResources()) { if (getContainedResources().getResourceId(next) != null) { continue; } 534 * encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true, 535 * fixContainedResourceId(next.getId().getValue())); } 536 */ 537 List<IBaseResource> containedResources = getContainedResources().getContainedResources(); 538 if (containedResources.size() > 0) { 539 beginArray(theEventWriter, theChildName); 540 541 for (IBaseResource next : containedResources) { 542 IIdType resourceId = getContainedResources().getResourceId(next); 543 encodeResourceToJsonStreamWriter(theResDef, next, theEventWriter, null, true, fixContainedResourceId(resourceId.getValue())); 544 } 545 546 theEventWriter.endArray(); 547 } 548 break; 549 } 550 case PRIMITIVE_XHTML_HL7ORG: 551 case PRIMITIVE_XHTML: { 552 if (!isSuppressNarratives()) { 553 IPrimitiveType<?> dt = (IPrimitiveType<?>) theNextValue; 554 if (theChildName != null) { 555 write(theEventWriter, theChildName, dt.getValueAsString()); 556 } else { 557 theEventWriter.write(dt.getValueAsString()); 558 } 559 } else { 560 if (theChildName != null) { 561 // do nothing 562 } else { 563 theEventWriter.writeNull(); 564 } 565 } 566 break; 567 } 568 case RESOURCE: 569 IBaseResource resource = (IBaseResource) theNextValue; 570 RuntimeResourceDefinition def = myContext.getResourceDefinition(resource); 571 encodeResourceToJsonStreamWriter(def, resource, theEventWriter, theChildName, false, true); 572 break; 573 case UNDECL_EXT: 574 default: 575 throw new IllegalStateException("Should not have this state here: " + theChildDef.getChildType().name()); 576 } 577 578 } 579 580 private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theElement, JsonLikeWriter theEventWriter, boolean theContainedResource, CompositeChildElement theParent) throws IOException { 581 582 { 583 String elementId = getCompositeElementId(theElement); 584 if (isNotBlank(elementId)) { 585 write(theEventWriter, "id", elementId); 586 } 587 } 588 589 boolean haveWrittenExtensions = false; 590 for (CompositeChildElement nextChildElem : super.compositeChildIterator(theElement, theContainedResource, theParent)) { 591 592 BaseRuntimeChildDefinition nextChild = nextChildElem.getDef(); 593 594 if (nextChildElem.getDef().getElementName().equals("extension") || nextChildElem.getDef().getElementName().equals("modifierExtension") || nextChild instanceof RuntimeChildDeclaredExtensionDefinition) { 595 if (!haveWrittenExtensions) { 596 extractAndWriteExtensionsAsDirectChild(theElement, theEventWriter, myContext.getElementDefinition(theElement.getClass()), theResDef, theResource, nextChildElem, theParent); 597 haveWrittenExtensions = true; 598 } 599 continue; 600 } 601 602 if (nextChild instanceof RuntimeChildNarrativeDefinition) { 603 INarrativeGenerator gen = myContext.getNarrativeGenerator(); 604 if (gen != null) { 605 INarrative narr; 606 if (theResource instanceof IResource) { 607 narr = ((IResource) theResource).getText(); 608 } else if (theResource instanceof IDomainResource) { 609 narr = ((IDomainResource) theResource).getText(); 610 } else { 611 narr = null; 612 } 613 if (narr != null && narr.isEmpty()) { 614 gen.generateNarrative(myContext, theResource, narr); 615 if (!narr.isEmpty()) { 616 RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild; 617 String childName = nextChild.getChildNameByDatatype(child.getDatatype()); 618 BaseRuntimeElementDefinition<?> type = child.getChildByName(childName); 619 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, narr, type, childName, theContainedResource, nextChildElem, false); 620 continue; 621 } 622 } 623 } 624 } else if (nextChild instanceof RuntimeChildContainedResources) { 625 String childName = nextChild.getValidChildNames().iterator().next(); 626 BaseRuntimeElementDefinition<?> child = nextChild.getChildByName(childName); 627 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, null, child, childName, theContainedResource, nextChildElem, false); 628 continue; 629 } 630 631 List<? extends IBase> values = nextChild.getAccessor().getValues(theElement); 632 values = super.preProcessValues(nextChild, theResource, values, nextChildElem); 633 634 if (values == null || values.isEmpty()) { 635 continue; 636 } 637 638 String currentChildName = null; 639 boolean inArray = false; 640 641 ArrayList<ArrayList<HeldExtension>> extensions = new ArrayList<ArrayList<HeldExtension>>(0); 642 ArrayList<ArrayList<HeldExtension>> modifierExtensions = new ArrayList<ArrayList<HeldExtension>>(0); 643 ArrayList<ArrayList<String>> comments = new ArrayList<ArrayList<String>>(0); 644 ArrayList<String> ids = new ArrayList<String>(0); 645 646 int valueIdx = 0; 647 for (IBase nextValue : values) { 648 649 if (nextValue == null || nextValue.isEmpty()) { 650 if (nextValue instanceof BaseContainedDt) { 651 if (theContainedResource || getContainedResources().isEmpty()) { 652 continue; 653 } 654 } else { 655 continue; 656 } 657 } 658 659 BaseParser.ChildNameAndDef childNameAndDef = super.getChildNameAndDef(nextChild, nextValue); 660 if (childNameAndDef == null) { 661 continue; 662 } 663 664 String childName = childNameAndDef.getChildName(); 665 BaseRuntimeElementDefinition<?> childDef = childNameAndDef.getChildDef(); 666 boolean primitive = childDef.getChildType() == ChildTypeEnum.PRIMITIVE_DATATYPE; 667 668 if ((childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCES || childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCE_LIST) && theContainedResource) { 669 continue; 670 } 671 672 boolean force = false; 673 if (primitive) { 674 if (nextValue instanceof ISupportsUndeclaredExtensions) { 675 List<ExtensionDt> ext = ((ISupportsUndeclaredExtensions) nextValue).getUndeclaredExtensions(); 676 force |= addToHeldExtensions(valueIdx, ext, extensions, false, nextChildElem, theParent); 677 678 ext = ((ISupportsUndeclaredExtensions) nextValue).getUndeclaredModifierExtensions(); 679 force |= addToHeldExtensions(valueIdx, ext, modifierExtensions, true, nextChildElem, theParent); 680 } else { 681 if (nextValue instanceof IBaseHasExtensions) { 682 IBaseHasExtensions element = (IBaseHasExtensions) nextValue; 683 List<? extends IBaseExtension<?, ?>> ext = element.getExtension(); 684 force |= addToHeldExtensions(valueIdx, ext, extensions, false, nextChildElem, theParent); 685 } 686 if (nextValue instanceof IBaseHasModifierExtensions) { 687 IBaseHasModifierExtensions element = (IBaseHasModifierExtensions) nextValue; 688 List<? extends IBaseExtension<?, ?>> ext = element.getModifierExtension(); 689 force |= addToHeldExtensions(valueIdx, ext, extensions, true, nextChildElem, theParent); 690 } 691 } 692 if (nextValue.hasFormatComment()) { 693 force |= addToHeldComments(valueIdx, nextValue.getFormatCommentsPre(), comments); 694 force |= addToHeldComments(valueIdx, nextValue.getFormatCommentsPost(), comments); 695 } 696 String elementId = getCompositeElementId(nextValue); 697 if (isNotBlank(elementId)) { 698 force = true; 699 addToHeldIds(valueIdx, ids, elementId); 700 } 701 } 702 703 if (currentChildName == null || !currentChildName.equals(childName)) { 704 if (inArray) { 705 theEventWriter.endArray(); 706 } 707 if (nextChild.getMax() > 1 || nextChild.getMax() == Child.MAX_UNLIMITED) { 708 beginArray(theEventWriter, childName); 709 inArray = true; 710 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theContainedResource, nextChildElem, force); 711 } else if (nextChild instanceof RuntimeChildNarrativeDefinition && theContainedResource) { 712 // suppress narratives from contained resources 713 } else { 714 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, childName, theContainedResource, nextChildElem, false); 715 } 716 currentChildName = childName; 717 } else { 718 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theContainedResource, nextChildElem, force); 719 } 720 721 valueIdx++; 722 } 723 724 if (inArray) { 725 theEventWriter.endArray(); 726 } 727 728 if (!extensions.isEmpty() || !modifierExtensions.isEmpty() || !comments.isEmpty()) { 729 if (inArray) { 730 // If this is a repeatable field, the extensions go in an array too 731 beginArray(theEventWriter, '_' + currentChildName); 732 } else { 733 beginObject(theEventWriter, '_' + currentChildName); 734 } 735 736 for (int i = 0; i < valueIdx; i++) { 737 boolean haveContent = false; 738 739 List<HeldExtension> heldExts = Collections.emptyList(); 740 List<HeldExtension> heldModExts = Collections.emptyList(); 741 if (extensions.size() > i && extensions.get(i) != null && extensions.get(i).isEmpty() == false) { 742 haveContent = true; 743 heldExts = extensions.get(i); 744 } 745 746 if (modifierExtensions.size() > i && modifierExtensions.get(i) != null && modifierExtensions.get(i).isEmpty() == false) { 747 haveContent = true; 748 heldModExts = modifierExtensions.get(i); 749 } 750 751 ArrayList<String> nextComments; 752 if (comments.size() > i) { 753 nextComments = comments.get(i); 754 } else { 755 nextComments = null; 756 } 757 if (nextComments != null && nextComments.isEmpty() == false) { 758 haveContent = true; 759 } 760 761 String elementId = null; 762 if (ids.size() > i) { 763 elementId = ids.get(i); 764 haveContent |= isNotBlank(elementId); 765 } 766 767 if (!haveContent) { 768 theEventWriter.writeNull(); 769 } else { 770 if (inArray) { 771 theEventWriter.beginObject(); 772 } 773 if (isNotBlank(elementId)) { 774 write(theEventWriter, "id", elementId); 775 } 776 if (nextComments != null && !nextComments.isEmpty()) { 777 beginArray(theEventWriter, "fhir_comments"); 778 for (String next : nextComments) { 779 theEventWriter.write(next); 780 } 781 theEventWriter.endArray(); 782 } 783 writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, heldExts, heldModExts); 784 if (inArray) { 785 theEventWriter.endObject(); 786 } 787 } 788 } 789 790 if (inArray) { 791 theEventWriter.endArray(); 792 } else { 793 theEventWriter.endObject(); 794 } 795 } 796 } 797 } 798 799 private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonLikeWriter theEventWriter, boolean theContainedResource, CompositeChildElement theParent) throws IOException, DataFormatException { 800 801 writeCommentsPreAndPost(theNextValue, theEventWriter); 802 encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, theContainedResource, theParent); 803 } 804 805 @Override 806 public void encodeResourceToJsonLikeWriter(IBaseResource theResource, JsonLikeWriter theJsonLikeWriter) throws IOException, DataFormatException { 807 Validate.notNull(theResource, "theResource can not be null"); 808 Validate.notNull(theJsonLikeWriter, "theJsonLikeWriter can not be null"); 809 810 if (theResource.getStructureFhirVersionEnum() != myContext.getVersion().getVersion()) { 811 throw new IllegalArgumentException("This parser is for FHIR version " + myContext.getVersion().getVersion() + " - Can not encode a structure for version " + theResource.getStructureFhirVersionEnum()); 812 } 813 814 doEncodeResourceToJsonLikeWriter(theResource, theJsonLikeWriter); 815 } 816 817 private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonLikeWriter theEventWriter, String theObjectNameOrNull, boolean theContainedResource, boolean theSubResource) throws IOException { 818 IIdType resourceId = null; 819 // if (theResource instanceof IResource) { 820 // IResource res = (IResource) theResource; 821 // if (StringUtils.isNotBlank(res.getId().getIdPart())) { 822 // if (theContainedResource) { 823 // resourceId = res.getId().getIdPart(); 824 // } else if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { 825 // resourceId = res.getId().getIdPart(); 826 // } 827 // } 828 // } else if (theResource instanceof IAnyResource) { 829 // IAnyResource res = (IAnyResource) theResource; 830 // if (/* theContainedResource && */StringUtils.isNotBlank(res.getIdElement().getIdPart())) { 831 // resourceId = res.getIdElement().getIdPart(); 832 // } 833 // } 834 835 if (StringUtils.isNotBlank(theResource.getIdElement().getIdPart())) { 836 resourceId = theResource.getIdElement(); 837 if (theResource.getIdElement().getValue().startsWith("urn:")) { 838 resourceId = null; 839 } 840 if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) { 841 resourceId = null; 842 } 843 } 844 845 if (!theContainedResource) { 846 if (super.shouldEncodeResourceId(theResource) == false) { 847 resourceId = null; 848 } else if (!theSubResource && getEncodeForceResourceId() != null) { 849 resourceId = getEncodeForceResourceId(); 850 } 851 } 852 853 encodeResourceToJsonStreamWriter(theResDef, theResource, theEventWriter, theObjectNameOrNull, theContainedResource, resourceId); 854 } 855 856 private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonLikeWriter theEventWriter, String theObjectNameOrNull, boolean theContainedResource, IIdType theResourceId) throws IOException { 857 if (!theContainedResource) { 858 super.containResourcesForEncoding(theResource); 859 } 860 861 RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource); 862 863 if (theObjectNameOrNull == null) { 864 theEventWriter.beginObject(); 865 } else { 866 beginObject(theEventWriter, theObjectNameOrNull); 867 } 868 869 write(theEventWriter, "resourceType", resDef.getName()); 870 if (theResourceId != null && theResourceId.hasIdPart()) { 871 write(theEventWriter, "id", theResourceId.getIdPart()); 872 if (theResourceId.hasFormatComment()) { 873 beginObject(theEventWriter, "_id"); 874 writeCommentsPreAndPost(theResourceId, theEventWriter); 875 theEventWriter.endObject(); 876 } 877 } 878 879 if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1) && theResource instanceof IResource) { 880 IResource resource = (IResource) theResource; 881 // Object securityLabelRawObj = 882 883 List<BaseCodingDt> securityLabels = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.SECURITY_LABELS); 884 List<? extends IIdType> profiles = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.PROFILES); 885 profiles = super.getProfileTagsForEncoding(resource, profiles); 886 887 TagList tags = getMetaTagsForEncoding(resource); 888 InstantDt updated = (InstantDt) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED); 889 IdDt resourceId = resource.getId(); 890 String versionIdPart = resourceId.getVersionIdPart(); 891 if (isBlank(versionIdPart)) { 892 versionIdPart = ResourceMetadataKeyEnum.VERSION.get(resource); 893 } 894 895 if (super.shouldEncodeResourceMeta(resource) && ElementUtil.isEmpty(versionIdPart, updated, securityLabels, tags, profiles) == false) { 896 beginObject(theEventWriter, "meta"); 897 writeOptionalTagWithTextNode(theEventWriter, "versionId", versionIdPart); 898 writeOptionalTagWithTextNode(theEventWriter, "lastUpdated", updated); 899 900 if (profiles != null && profiles.isEmpty() == false) { 901 beginArray(theEventWriter, "profile"); 902 for (IIdType profile : profiles) { 903 if (profile != null && isNotBlank(profile.getValue())) { 904 theEventWriter.write(profile.getValue()); 905 } 906 } 907 theEventWriter.endArray(); 908 } 909 910 if (securityLabels.isEmpty() == false) { 911 beginArray(theEventWriter, "security"); 912 for (BaseCodingDt securityLabel : securityLabels) { 913 theEventWriter.beginObject(); 914 encodeCompositeElementChildrenToStreamWriter(resDef, resource, securityLabel, theEventWriter, theContainedResource, null); 915 theEventWriter.endObject(); 916 } 917 theEventWriter.endArray(); 918 } 919 920 if (tags != null && tags.isEmpty() == false) { 921 beginArray(theEventWriter, "tag"); 922 for (Tag tag : tags) { 923 if (tag.isEmpty()) { 924 continue; 925 } 926 theEventWriter.beginObject(); 927 writeOptionalTagWithTextNode(theEventWriter, "system", tag.getScheme()); 928 writeOptionalTagWithTextNode(theEventWriter, "code", tag.getTerm()); 929 writeOptionalTagWithTextNode(theEventWriter, "display", tag.getLabel()); 930 theEventWriter.endObject(); 931 } 932 theEventWriter.endArray(); 933 } 934 935 theEventWriter.endObject(); // end meta 936 } 937 } 938 939 if (theResource instanceof IBaseBinary) { 940 IBaseBinary bin = (IBaseBinary) theResource; 941 String contentType = bin.getContentType(); 942 if (isNotBlank(contentType)) { 943 write(theEventWriter, "contentType", contentType); 944 } 945 String contentAsBase64 = bin.getContentAsBase64(); 946 if (isNotBlank(contentAsBase64)) { 947 write(theEventWriter, "content", contentAsBase64); 948 } 949 } else { 950 encodeCompositeElementToStreamWriter(theResDef, theResource, theResource, theEventWriter, theContainedResource, new CompositeChildElement(resDef)); 951 } 952 953 theEventWriter.endObject(); 954 } 955 956 @Override 957 public void encodeTagListToWriter(TagList theTagList, Writer theWriter) throws IOException { 958 JsonLikeWriter theEventWriter = createJsonWriter(theWriter); 959 encodeTagListToJsonLikeWriter(theTagList, theEventWriter); 960 } 961 962 @Override 963 public void encodeTagListToJsonLikeWriter(TagList theTagList, JsonLikeWriter theEventWriter) throws IOException { 964 if (myPrettyPrint) { 965 theEventWriter.setPrettyPrint(myPrettyPrint); 966 } 967 theEventWriter.init(); 968 969 theEventWriter.beginObject(); 970 971 write(theEventWriter, "resourceType", TagList.ELEMENT_NAME); 972 973 beginArray(theEventWriter, TagList.ATTR_CATEGORY); 974 for (Tag next : theTagList) { 975 theEventWriter.beginObject(); 976 977 if (isNotBlank(next.getTerm())) { 978 write(theEventWriter, Tag.ATTR_TERM, next.getTerm()); 979 } 980 if (isNotBlank(next.getLabel())) { 981 write(theEventWriter, Tag.ATTR_LABEL, next.getLabel()); 982 } 983 if (isNotBlank(next.getScheme())) { 984 write(theEventWriter, Tag.ATTR_SCHEME, next.getScheme()); 985 } 986 987 theEventWriter.endObject(); 988 } 989 theEventWriter.endArray(); 990 991 theEventWriter.endObject(); 992 theEventWriter.flush(); 993 } 994 995 /** 996 * This is useful only for the two cases where extensions are encoded as direct children (e.g. not in some object 997 * called _name): resource extensions, and extension extensions 998 * @param theChildElem 999 * @param theParent 1000 */ 1001 private void extractAndWriteExtensionsAsDirectChild(IBase theElement, JsonLikeWriter theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef, IBaseResource theResource, CompositeChildElement theChildElem, CompositeChildElement theParent) throws IOException { 1002 List<HeldExtension> extensions = new ArrayList<HeldExtension>(0); 1003 List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0); 1004 1005 // Undeclared extensions 1006 extractUndeclaredExtensions(theElement, extensions, modifierExtensions, theChildElem, theParent); 1007 1008 // Declared extensions 1009 if (theElementDef != null) { 1010 extractDeclaredExtensions(theElement, theElementDef, extensions, modifierExtensions, theChildElem); 1011 } 1012 1013 // Write the extensions 1014 writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, extensions, modifierExtensions); 1015 } 1016 1017 private void extractDeclaredExtensions(IBase theResource, BaseRuntimeElementDefinition<?> resDef, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions, CompositeChildElement theChildElem) { 1018 for (RuntimeChildDeclaredExtensionDefinition nextDef : resDef.getExtensionsNonModifier()) { 1019 for (IBase nextValue : nextDef.getAccessor().getValues(theResource)) { 1020 if (nextValue != null) { 1021 if (nextValue.isEmpty()) { 1022 continue; 1023 } 1024 extensions.add(new HeldExtension(nextDef, nextValue, theChildElem)); 1025 } 1026 } 1027 } 1028 for (RuntimeChildDeclaredExtensionDefinition nextDef : resDef.getExtensionsModifier()) { 1029 for (IBase nextValue : nextDef.getAccessor().getValues(theResource)) { 1030 if (nextValue != null) { 1031 if (nextValue.isEmpty()) { 1032 continue; 1033 } 1034 modifierExtensions.add(new HeldExtension(nextDef, nextValue, theChildElem)); 1035 } 1036 } 1037 } 1038 } 1039 1040 private void extractUndeclaredExtensions(IBase theElement, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions, CompositeChildElement theChildElem, CompositeChildElement theParent) { 1041 if (theElement instanceof ISupportsUndeclaredExtensions) { 1042 ISupportsUndeclaredExtensions element = (ISupportsUndeclaredExtensions) theElement; 1043 List<ExtensionDt> ext = element.getUndeclaredExtensions(); 1044 for (ExtensionDt next : ext) { 1045 if (next == null || next.isEmpty()) { 1046 continue; 1047 } 1048 extensions.add(new HeldExtension(next, false, theChildElem, theParent)); 1049 } 1050 1051 ext = element.getUndeclaredModifierExtensions(); 1052 for (ExtensionDt next : ext) { 1053 if (next == null || next.isEmpty()) { 1054 continue; 1055 } 1056 modifierExtensions.add(new HeldExtension(next, true, theChildElem, theParent)); 1057 } 1058 } else { 1059 if (theElement instanceof IBaseHasExtensions) { 1060 IBaseHasExtensions element = (IBaseHasExtensions) theElement; 1061 List<? extends IBaseExtension<?, ?>> ext = element.getExtension(); 1062 for (IBaseExtension<?, ?> next : ext) { 1063 if (next == null || (ElementUtil.isEmpty(next.getValue()) && next.getExtension().isEmpty())) { 1064 continue; 1065 } 1066 extensions.add(new HeldExtension(next, false, theChildElem, theParent)); 1067 } 1068 } 1069 if (theElement instanceof IBaseHasModifierExtensions) { 1070 IBaseHasModifierExtensions element = (IBaseHasModifierExtensions) theElement; 1071 List<? extends IBaseExtension<?, ?>> ext = element.getModifierExtension(); 1072 for (IBaseExtension<?, ?> next : ext) { 1073 if (next == null || next.isEmpty()) { 1074 continue; 1075 } 1076 modifierExtensions.add(new HeldExtension(next, true, theChildElem, theParent)); 1077 } 1078 } 1079 } 1080 } 1081 1082 @Override 1083 public EncodingEnum getEncoding() { 1084 return EncodingEnum.JSON; 1085 } 1086 1087 private JsonLikeArray grabJsonArray(JsonLikeObject theObject, String nextName, String thePosition) { 1088 JsonLikeValue object = theObject.get(nextName); 1089 if (object == null || object.isNull()) { 1090 return null; 1091 } 1092 if (!object.isArray()) { 1093 throw new DataFormatException("Syntax error parsing JSON FHIR structure: Expected ARRAY at element '" + thePosition + "', found '" + object.getJsonType() + "'"); 1094 } 1095 return object.getAsArray(); 1096 } 1097 1098// private JsonObject parse(Reader theReader) { 1099// 1100// PushbackReader pbr = new PushbackReader(theReader); 1101// JsonObject object; 1102// try { 1103// while(true) { 1104// int nextInt; 1105// nextInt = pbr.read(); 1106// if (nextInt == -1) { 1107// throw new DataFormatException("Did not find any content to parse"); 1108// } 1109// if (nextInt == '{') { 1110// pbr.unread('{'); 1111// break; 1112// } 1113// if (Character.isWhitespace(nextInt)) { 1114// continue; 1115// } 1116// throw new DataFormatException("Content does not appear to be FHIR JSON, first non-whitespace character was: '" + (char)nextInt + "' (must be '{')"); 1117// } 1118// 1119// Gson gson = newGson(); 1120// 1121// object = gson.fromJson(pbr, JsonObject.class); 1122// } catch (Exception e) { 1123// throw new DataFormatException("Failed to parse JSON content, error was: " + e.getMessage(), e); 1124// } 1125// 1126// return object; 1127// } 1128 1129 private void parseAlternates(JsonLikeValue theAlternateVal, ParserState<?> theState, String theElementName, String theAlternateName) { 1130 if (theAlternateVal == null || theAlternateVal.isNull()) { 1131 return; 1132 } 1133 1134 if (theAlternateVal.isArray()) { 1135 JsonLikeArray array = theAlternateVal.getAsArray(); 1136 if (array.size() > 1) { 1137 throw new DataFormatException("Unexpected array of length " + array.size() + " (expected 0 or 1) for element: " + theElementName); 1138 } 1139 if (array.size() == 0) { 1140 return; 1141 } 1142 parseAlternates(array.get(0), theState, theElementName, theAlternateName); 1143 return; 1144 } 1145 1146 JsonLikeValue alternateVal = theAlternateVal; 1147 if (alternateVal.isObject() == false) { 1148 getErrorHandler().incorrectJsonType(null, theAlternateName, ValueType.OBJECT, null, alternateVal.getJsonType(), null); 1149 return; 1150 } 1151 1152 JsonLikeObject alternate = alternateVal.getAsObject(); 1153 for (String nextKey : alternate.keySet()) { 1154 JsonLikeValue nextVal = alternate.get(nextKey); 1155 if ("extension".equals(nextKey)) { 1156 boolean isModifier = false; 1157 JsonLikeArray array = nextVal.getAsArray(); 1158 parseExtension(theState, array, isModifier); 1159 } else if ("modifierExtension".equals(nextKey)) { 1160 boolean isModifier = true; 1161 JsonLikeArray array = nextVal.getAsArray(); 1162 parseExtension(theState, array, isModifier); 1163 } else if ("id".equals(nextKey)) { 1164 if (nextVal.isString()) { 1165 theState.attributeValue("id", nextVal.getAsString()); 1166 } else { 1167 getErrorHandler().incorrectJsonType(null, "id", ValueType.SCALAR, ScalarType.STRING, nextVal.getJsonType(), nextVal.getDataType()); 1168 } 1169 } else if ("fhir_comments".equals(nextKey)) { 1170 parseFhirComments(nextVal, theState); 1171 } 1172 } 1173 } 1174 1175 @Override 1176 public <T extends IBaseResource> Bundle parseBundle(Class<T> theResourceType, Reader theReader) { 1177 JsonLikeStructure jsonStructure = new GsonStructure(); 1178 jsonStructure.load(theReader); 1179 1180 Bundle retVal = parseBundle(theResourceType, jsonStructure); 1181 1182 return retVal; 1183 } 1184 1185 @Override 1186 public Bundle parseBundle(JsonLikeStructure theJsonLikeStructure) throws DataFormatException { 1187 return parseBundle(null, theJsonLikeStructure); 1188 } 1189 1190 @Override 1191 public <T extends IBaseResource> Bundle parseBundle(Class<T> theResourceType, JsonLikeStructure theJsonStructure) { 1192 JsonLikeObject object = theJsonStructure.getRootObject(); 1193 1194 JsonLikeValue resourceTypeObj = object.get("resourceType"); 1195 if (resourceTypeObj == null || !resourceTypeObj.isString()) { 1196 throw new DataFormatException("Invalid JSON content detected, missing required element: 'resourceType'"); 1197 } 1198 String resourceType = resourceTypeObj.getAsString(); 1199 if (!"Bundle".equals(resourceType)) { 1200 throw new DataFormatException("Trying to parse bundle but found resourceType other than 'Bundle'. Found: '" + resourceType + "'"); 1201 } 1202 1203 ParserState<Bundle> state = ParserState.getPreAtomInstance(this, myContext, theResourceType, true, getErrorHandler()); 1204 if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { 1205 state.enteringNewElement(null, "Bundle"); 1206 } else { 1207 state.enteringNewElement(null, "feed"); 1208 } 1209 1210 parseBundleChildren(object, state); 1211 1212 state.endingElement(); 1213 state.endingElement(); 1214 1215 Bundle retVal = state.getObject(); 1216 1217 return retVal; 1218 } 1219 1220 private void parseBundleChildren(JsonLikeObject theObject, ParserState<?> theState) { 1221 for (String nextName : theObject.keySet()) { 1222 if ("resourceType".equals(nextName)) { 1223 continue; 1224 } else if ("entry".equals(nextName)) { 1225 JsonLikeArray entries = grabJsonArray(theObject, nextName, "entry"); 1226 for (int i = 0; i < entries.size(); i++) { 1227 JsonLikeValue jsonValue = entries.get(i); 1228 theState.enteringNewElement(null, "entry"); 1229 parseBundleChildren(jsonValue.getAsObject(), theState); 1230 theState.endingElement(); 1231 } 1232 continue; 1233 } else if (myContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) { 1234 if ("link".equals(nextName)) { 1235 JsonLikeArray entries = grabJsonArray(theObject, nextName, "link"); 1236 for (int i = 0; i < entries.size(); i++) { 1237 JsonLikeValue jsonValue = entries.get(i); 1238 theState.enteringNewElement(null, "link"); 1239 JsonLikeObject linkObj = jsonValue.getAsObject(); 1240 String rel = linkObj.getString("rel", null); 1241 String href = linkObj.getString("href", null); 1242 theState.attributeValue("rel", rel); 1243 theState.attributeValue("href", href); 1244 theState.endingElement(); 1245 } 1246 continue; 1247 } else if (BUNDLE_TEXTNODE_CHILDREN_DSTU1.contains(nextName)) { 1248 theState.enteringNewElement(null, nextName); 1249 JsonLikeValue jsonElement = theObject.get(nextName); 1250 if (jsonElement.isScalar()) { 1251 theState.string(jsonElement.getAsString()); 1252 } 1253 theState.endingElement(); 1254 continue; 1255 } 1256 } else { 1257 if ("link".equals(nextName)) { 1258 JsonLikeArray entries = grabJsonArray(theObject, nextName, "link"); 1259 for (int i = 0; i < entries.size(); i++) { 1260 JsonLikeValue jsonValue = entries.get(i); 1261 theState.enteringNewElement(null, "link"); 1262 JsonLikeObject linkObj = jsonValue.getAsObject(); 1263 String rel = linkObj.getString("relation", null); 1264 String href = linkObj.getString("url", null); 1265 theState.enteringNewElement(null, "relation"); 1266 theState.attributeValue("value", rel); 1267 theState.endingElement(); 1268 theState.enteringNewElement(null, "url"); 1269 theState.attributeValue("value", href); 1270 theState.endingElement(); 1271 theState.endingElement(); 1272 } 1273 continue; 1274 } else if (BUNDLE_TEXTNODE_CHILDREN_DSTU2.contains(nextName)) { 1275 theState.enteringNewElement(null, nextName); 1276 // String obj = theObject.getString(nextName, null); 1277 1278 JsonLikeValue obj = theObject.get(nextName); 1279 if (obj == null || obj.isNull()) { 1280 theState.attributeValue("value", null); 1281 } else if (obj.isScalar()) { 1282 theState.attributeValue("value", obj.getAsString()); 1283 } else { 1284 throw new DataFormatException("Unexpected JSON object for entry '" + nextName + "'"); 1285 } 1286 1287 theState.endingElement(); 1288 continue; 1289 } 1290 } 1291 1292 JsonLikeValue nextVal = theObject.get(nextName); 1293 parseChildren(theState, nextName, nextVal, null, null, false); 1294 1295 } 1296 } 1297 1298 private void parseChildren(JsonLikeObject theObject, ParserState<?> theState) { 1299 Set<String> keySet = theObject.keySet(); 1300 1301 int allUnderscoreNames = 0; 1302 int handledUnderscoreNames = 0; 1303 1304 for (String nextName : keySet) { 1305 if ("resourceType".equals(nextName)) { 1306 continue; 1307 } else if ("extension".equals(nextName)) { 1308 JsonLikeArray array = grabJsonArray(theObject, nextName, "extension"); 1309 parseExtension(theState, array, false); 1310 continue; 1311 } else if ("modifierExtension".equals(nextName)) { 1312 JsonLikeArray array = grabJsonArray(theObject, nextName, "modifierExtension"); 1313 parseExtension(theState, array, true); 1314 continue; 1315 } else if (nextName.equals("fhir_comments")) { 1316 parseFhirComments(theObject.get(nextName), theState); 1317 continue; 1318 } else if (nextName.charAt(0) == '_') { 1319 allUnderscoreNames++; 1320 continue; 1321 } 1322 1323 JsonLikeValue nextVal = theObject.get(nextName); 1324 String alternateName = '_' + nextName; 1325 JsonLikeValue alternateVal = theObject.get(alternateName); 1326 if (alternateVal != null) { 1327 handledUnderscoreNames++; 1328 } 1329 1330 parseChildren(theState, nextName, nextVal, alternateVal, alternateName, false); 1331 1332 } 1333 1334// if (elementId != null) { 1335// IBase object = (IBase) theState.getObject(); 1336// if (object instanceof IIdentifiableElement) { 1337// ((IIdentifiableElement) object).setElementSpecificId(elementId); 1338// } else if (object instanceof IBaseResource) { 1339// ((IBaseResource) object).getIdElement().setValue(elementId); 1340// } 1341// } 1342 1343 /* 1344 * This happens if an element has an extension but no actual value. I.e. 1345 * if a resource has a "_status" element but no corresponding "status" 1346 * element. This could be used to handle a null value with an extension 1347 * for example. 1348 */ 1349 if (allUnderscoreNames > handledUnderscoreNames) { 1350 for (String alternateName : keySet) { 1351 if (alternateName.startsWith("_") && alternateName.length() > 1) { 1352 JsonLikeValue nextValue = theObject.get(alternateName); 1353 if (nextValue != null) { 1354 if (nextValue.isObject()) { 1355 String nextName = alternateName.substring(1); 1356 if (theObject.get(nextName) == null) { 1357 theState.enteringNewElement(null, nextName); 1358 parseAlternates(nextValue, theState, alternateName, alternateName); 1359 theState.endingElement(); 1360 } 1361 } else { 1362 getErrorHandler().incorrectJsonType(null, alternateName, ValueType.OBJECT, null, nextValue.getJsonType(), null); 1363 } 1364 } 1365 } 1366 } 1367 } 1368 1369 } 1370 1371 private void parseChildren(ParserState<?> theState, String theName, JsonLikeValue theJsonVal, JsonLikeValue theAlternateVal, String theAlternateName, boolean theInArray) { 1372 if (theName.equals("id")) { 1373 if (!theJsonVal.isString()) { 1374 getErrorHandler().incorrectJsonType(null, "id", ValueType.SCALAR, ScalarType.STRING, theJsonVal.getJsonType(), theJsonVal.getDataType()); 1375 } 1376 } 1377 1378 if (theJsonVal.isArray()) { 1379 JsonLikeArray nextArray = theJsonVal.getAsArray(); 1380 1381 JsonLikeValue alternateVal = theAlternateVal; 1382 if (alternateVal != null && alternateVal.isArray() == false) { 1383 getErrorHandler().incorrectJsonType(null, theAlternateName, ValueType.ARRAY, null, alternateVal.getJsonType(), null); 1384 alternateVal = null; 1385 } 1386 1387 JsonLikeArray nextAlternateArray = JsonLikeValue.asArray(alternateVal); // could be null 1388 for (int i = 0; i < nextArray.size(); i++) { 1389 JsonLikeValue nextObject = nextArray.get(i); 1390 JsonLikeValue nextAlternate = null; 1391 if (nextAlternateArray != null) { 1392 nextAlternate = nextAlternateArray.get(i); 1393 } 1394 parseChildren(theState, theName, nextObject, nextAlternate, theAlternateName, true); 1395 } 1396 } else if (theJsonVal.isObject()) { 1397 if (!theInArray && theState.elementIsRepeating(theName)) { 1398 getErrorHandler().incorrectJsonType(null, theName, ValueType.ARRAY, null, ValueType.OBJECT, null); 1399 } 1400 1401 theState.enteringNewElement(null, theName); 1402 parseAlternates(theAlternateVal, theState, theAlternateName, theAlternateName); 1403 JsonLikeObject nextObject = theJsonVal.getAsObject(); 1404 boolean preResource = false; 1405 if (theState.isPreResource()) { 1406 JsonLikeValue resType = nextObject.get("resourceType"); 1407 if (resType == null || !resType.isString()) { 1408 throw new DataFormatException("Missing required element 'resourceType' from JSON resource object, unable to parse"); 1409 } 1410 theState.enteringNewElement(null, resType.getAsString()); 1411 preResource = true; 1412 } 1413 parseChildren(nextObject, theState); 1414 if (preResource) { 1415 theState.endingElement(); 1416 } 1417 theState.endingElement(); 1418 } else if (theJsonVal.isNull()) { 1419 theState.enteringNewElement(null, theName); 1420 parseAlternates(theAlternateVal, theState, theAlternateName, theAlternateName); 1421 theState.endingElement(); 1422 } else { 1423 // must be a SCALAR 1424 theState.enteringNewElement(null, theName); 1425 theState.attributeValue("value", theJsonVal.getAsString()); 1426 parseAlternates(theAlternateVal, theState, theAlternateName, theAlternateName); 1427 theState.endingElement(); 1428 } 1429 } 1430 1431 private void parseExtension(ParserState<?> theState, JsonLikeArray theValues, boolean theIsModifier) { 1432 for (int i = 0; i < theValues.size(); i++) { 1433 JsonLikeObject nextExtObj = JsonLikeValue.asObject(theValues.get(i)); 1434 JsonLikeValue jsonElement = nextExtObj.get("url"); 1435 String url; 1436 if (null == jsonElement || !(jsonElement.isScalar())) { 1437 String parentElementName; 1438 if (theIsModifier) { 1439 parentElementName = "modifierExtension"; 1440 } else { 1441 parentElementName = "extension"; 1442 } 1443 getErrorHandler().missingRequiredElement(new ParseLocation(parentElementName), "url"); 1444 url = null; 1445 } else { 1446 url = getExtensionUrl(jsonElement.getAsString()); 1447 } 1448 theState.enteringNewElementExtension(null, url, theIsModifier, getServerBaseUrl()); 1449 for (String next : nextExtObj.keySet()) { 1450 if ("url".equals(next)) { 1451 continue; 1452 } else if ("extension".equals(next)) { 1453 JsonLikeArray jsonVal = JsonLikeValue.asArray(nextExtObj.get(next)); 1454 parseExtension(theState, jsonVal, false); 1455 } else if ("modifierExtension".equals(next)) { 1456 JsonLikeArray jsonVal = JsonLikeValue.asArray(nextExtObj.get(next)); 1457 parseExtension(theState, jsonVal, true); 1458 } else { 1459 JsonLikeValue jsonVal = nextExtObj.get(next); 1460 parseChildren(theState, next, jsonVal, null, null, false); 1461 } 1462 } 1463 theState.endingElement(); 1464 } 1465 } 1466 1467 private void parseFhirComments(JsonLikeValue theObject, ParserState<?> theState) { 1468 if (theObject.isArray()) { 1469 JsonLikeArray comments = theObject.getAsArray(); 1470 for (int i = 0; i < comments.size(); i++) { 1471 JsonLikeValue nextComment = comments.get(i); 1472 if (nextComment.isString()) { 1473 String commentText = nextComment.getAsString(); 1474 if (commentText != null) { 1475 theState.commentPre(commentText); 1476 } 1477 } 1478 } 1479 } 1480 } 1481 1482 @Override 1483 public <T extends IBaseResource> T parseResource(Class<T> theResourceType, JsonLikeStructure theJsonLikeStructure) throws DataFormatException { 1484 1485 /***************************************************** 1486 * ************************************************* * 1487 * ** NOTE: this duplicates most of the code in ** * 1488 * ** BaseParser.parseResource(Class<T>, Reader). ** * 1489 * ** Unfortunately, there is no way to avoid ** * 1490 * ** this without doing some refactoring of the ** * 1491 * ** BaseParser class. ** * 1492 * ************************************************* * 1493 *****************************************************/ 1494 1495 /* 1496 * We do this so that the context can verify that the structure is for 1497 * the correct FHIR version 1498 */ 1499 if (theResourceType != null) { 1500 myContext.getResourceDefinition(theResourceType); 1501 } 1502 1503 // Actually do the parse 1504 T retVal = doParseResource(theResourceType, theJsonLikeStructure); 1505 1506 RuntimeResourceDefinition def = myContext.getResourceDefinition(retVal); 1507 if ("Bundle".equals(def.getName())) { 1508 1509 BaseRuntimeChildDefinition entryChild = def.getChildByName("entry"); 1510 BaseRuntimeElementCompositeDefinition<?> entryDef = (BaseRuntimeElementCompositeDefinition<?>) entryChild.getChildByName("entry"); 1511 List<IBase> entries = entryChild.getAccessor().getValues(retVal); 1512 if (entries != null) { 1513 for (IBase nextEntry : entries) { 1514 1515 /** 1516 * If Bundle.entry.fullUrl is populated, set the resource ID to that 1517 */ 1518 // TODO: should emit a warning and maybe notify the error handler if the resource ID doesn't match the 1519 // fullUrl idPart 1520 BaseRuntimeChildDefinition fullUrlChild = entryDef.getChildByName("fullUrl"); 1521 if (fullUrlChild == null) { 1522 continue; // TODO: remove this once the data model in tinder plugin catches up to 1.2 1523 } 1524 List<IBase> fullUrl = fullUrlChild.getAccessor().getValues(nextEntry); 1525 if (fullUrl != null && !fullUrl.isEmpty()) { 1526 IPrimitiveType<?> value = (IPrimitiveType<?>) fullUrl.get(0); 1527 if (value.isEmpty() == false) { 1528 List<IBase> entryResources = entryDef.getChildByName("resource").getAccessor().getValues(nextEntry); 1529 if (entryResources != null && entryResources.size() > 0) { 1530 IBaseResource res = (IBaseResource) entryResources.get(0); 1531 String versionId = res.getIdElement().getVersionIdPart(); 1532 res.setId(value.getValueAsString()); 1533 if (isNotBlank(versionId) && res.getIdElement().hasVersionIdPart() == false) { 1534 res.setId(res.getIdElement().withVersion(versionId)); 1535 } 1536 } 1537 } 1538 } 1539 1540 } 1541 } 1542 1543 } 1544 1545 return retVal; 1546 } 1547 1548 @Override 1549 public IBaseResource parseResource(JsonLikeStructure theJsonLikeStructure) throws DataFormatException { 1550 return parseResource(null, theJsonLikeStructure); 1551 } 1552 1553 @Override 1554 public TagList parseTagList(Reader theReader) { 1555 JsonLikeStructure jsonStructure = new GsonStructure(); 1556 jsonStructure.load(theReader); 1557 1558 TagList retVal = parseTagList(jsonStructure); 1559 1560 return retVal; 1561 } 1562 1563 @Override 1564 public TagList parseTagList(JsonLikeStructure theJsonStructure) { 1565 JsonLikeObject object = theJsonStructure.getRootObject(); 1566 1567 JsonLikeValue resourceTypeObj = object.get("resourceType"); 1568 String resourceType = resourceTypeObj.getAsString(); 1569 1570 ParserState<TagList> state = ParserState.getPreTagListInstance(this, myContext, true, getErrorHandler()); 1571 state.enteringNewElement(null, resourceType); 1572 1573 parseChildren(object, state); 1574 1575 state.endingElement(); 1576 state.endingElement(); 1577 1578 return state.getObject(); 1579 } 1580 1581 @Override 1582 public IParser setPrettyPrint(boolean thePrettyPrint) { 1583 myPrettyPrint = thePrettyPrint; 1584 return this; 1585 } 1586 1587 private void write(JsonLikeWriter theEventWriter, String theChildName, Boolean theValue) throws IOException { 1588 if (theValue != null) { 1589 theEventWriter.write(theChildName, theValue.booleanValue()); 1590 } 1591 } 1592 1593 // private void parseExtensionInDstu2Style(boolean theModifier, ParserState<?> theState, String 1594 // theParentExtensionUrl, String theExtensionUrl, JsonArray theValues) { 1595 // String extUrl = UrlUtil.constructAbsoluteUrl(theParentExtensionUrl, theExtensionUrl); 1596 // theState.enteringNewElementExtension(null, extUrl, theModifier); 1597 // 1598 // for (int extIdx = 0; extIdx < theValues.size(); extIdx++) { 1599 // JsonObject nextExt = theValues.getJsonObject(extIdx); 1600 // for (String nextKey : nextExt.keySet()) { 1601 // // if (nextKey.startsWith("value") && nextKey.length() > 5 && 1602 // // myContext.getRuntimeChildUndeclaredExtensionDefinition().getChildByName(nextKey) != null) { 1603 // JsonElement jsonVal = nextExt.get(nextKey); 1604 // if (jsonVal.getValueType() == ValueType.ARRAY) { 1605 // /* 1606 // * Extension children which are arrays are sub-extensions. Any other value type should be treated as a value. 1607 // */ 1608 // JsonArray arrayValue = (JsonArray) jsonVal; 1609 // parseExtensionInDstu2Style(theModifier, theState, extUrl, nextKey, arrayValue); 1610 // } else { 1611 // parseChildren(theState, nextKey, jsonVal, null, null); 1612 // } 1613 // } 1614 // } 1615 // 1616 // theState.endingElement(); 1617 // } 1618 1619 private void write(JsonLikeWriter theEventWriter, String theChildName, BigDecimal theDecimalValue) throws IOException { 1620 theEventWriter.write(theChildName, theDecimalValue); 1621 } 1622 1623 private void write(JsonLikeWriter theEventWriter, String theChildName, Integer theValue) throws IOException { 1624 theEventWriter.write(theChildName, theValue); 1625 } 1626 1627 private boolean writeAtomLinkInDstu1Format(JsonLikeWriter theEventWriter, String theRel, StringDt theLink, boolean theStarted) throws IOException { 1628 boolean retVal = theStarted; 1629 if (isNotBlank(theLink.getValue())) { 1630 if (theStarted == false) { 1631 theEventWriter.beginArray("link"); 1632 retVal = true; 1633 } 1634 1635 theEventWriter.beginObject(); 1636 write(theEventWriter, "rel", theRel); 1637 write(theEventWriter, "href", theLink.getValue()); 1638 theEventWriter.endObject(); 1639 } 1640 return retVal; 1641 } 1642 1643 private boolean writeAtomLinkInDstu2Format(JsonLikeWriter theEventWriter, String theRel, StringDt theLink, boolean theStarted) throws IOException { 1644 boolean retVal = theStarted; 1645 if (isNotBlank(theLink.getValue())) { 1646 if (theStarted == false) { 1647 theEventWriter.beginArray("link"); 1648 retVal = true; 1649 } 1650 1651 theEventWriter.beginObject(); 1652 write(theEventWriter, "relation", theRel); 1653 write(theEventWriter, "url", theLink.getValue()); 1654 theEventWriter.endObject(); 1655 } 1656 return retVal; 1657 } 1658 1659 private void writeAuthor(BaseBundle theBundle, JsonLikeWriter theEventWriter) throws IOException { 1660 if (StringUtils.isNotBlank(theBundle.getAuthorName().getValue())) { 1661 beginArray(theEventWriter, "author"); 1662 theEventWriter.beginObject(); 1663 writeTagWithTextNode(theEventWriter, "name", theBundle.getAuthorName()); 1664 writeOptionalTagWithTextNode(theEventWriter, "uri", theBundle.getAuthorUri()); 1665 theEventWriter.endObject(); 1666 theEventWriter.endArray(); 1667 } 1668 } 1669 1670 private void writeCategories(JsonLikeWriter theEventWriter, TagList categories) throws IOException { 1671 if (categories != null && categories.size() > 0) { 1672 theEventWriter.beginArray("category"); 1673 for (Tag next : categories) { 1674 theEventWriter.beginObject(); 1675 write(theEventWriter, "term", defaultString(next.getTerm())); 1676 write(theEventWriter, "label", defaultString(next.getLabel())); 1677 write(theEventWriter, "scheme", defaultString(next.getScheme())); 1678 theEventWriter.endObject(); 1679 } 1680 theEventWriter.endArray(); 1681 } 1682 } 1683 1684 private void writeCommentsPreAndPost(IBase theNextValue, JsonLikeWriter theEventWriter) throws IOException { 1685 if (theNextValue.hasFormatComment()) { 1686 beginArray(theEventWriter, "fhir_comments"); 1687 List<String> pre = theNextValue.getFormatCommentsPre(); 1688 if (pre.isEmpty() == false) { 1689 for (String next : pre) { 1690 theEventWriter.write(next); 1691 } 1692 } 1693 List<String> post = theNextValue.getFormatCommentsPost(); 1694 if (post.isEmpty() == false) { 1695 for (String next : post) { 1696 theEventWriter.write(next); 1697 } 1698 } 1699 theEventWriter.endArray(); 1700 } 1701 } 1702 1703 private void writeExtensionsAsDirectChild(IBaseResource theResource, JsonLikeWriter theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions) throws IOException { 1704 if (extensions.isEmpty() == false) { 1705 beginArray(theEventWriter, "extension"); 1706 for (HeldExtension next : extensions) { 1707 next.write(resDef, theResource, theEventWriter); 1708 } 1709 theEventWriter.endArray(); 1710 } 1711 if (modifierExtensions.isEmpty() == false) { 1712 beginArray(theEventWriter, "modifierExtension"); 1713 for (HeldExtension next : modifierExtensions) { 1714 next.write(resDef, theResource, theEventWriter); 1715 } 1716 theEventWriter.endArray(); 1717 } 1718 } 1719 1720 private void writeOptionalTagWithDecimalNode(JsonLikeWriter theEventWriter, String theElementName, DecimalDt theValue) throws IOException { 1721 if (theValue != null && theValue.isEmpty() == false) { 1722 write(theEventWriter, theElementName, theValue.getValue()); 1723 } 1724 } 1725 1726 private void writeOptionalTagWithNumberNode(JsonLikeWriter theEventWriter, String theElementName, IntegerDt theValue) throws IOException { 1727 if (theValue != null && theValue.isEmpty() == false) { 1728 write(theEventWriter, theElementName, theValue.getValue().intValue()); 1729 } 1730 } 1731 1732 private void writeOptionalTagWithTextNode(JsonLikeWriter theEventWriter, String theElementName, IPrimitiveDatatype<?> thePrimitive) throws IOException { 1733 if (thePrimitive == null) { 1734 return; 1735 } 1736 String str = thePrimitive.getValueAsString(); 1737 writeOptionalTagWithTextNode(theEventWriter, theElementName, str); 1738 } 1739 1740 private void writeOptionalTagWithTextNode(JsonLikeWriter theEventWriter, String theElementName, String theValue) throws IOException { 1741 if (StringUtils.isNotBlank(theValue)) { 1742 write(theEventWriter, theElementName, theValue); 1743 } 1744 } 1745 1746 private void writeTagWithTextNode(JsonLikeWriter theEventWriter, String theElementName, IPrimitiveDatatype<?> theIdDt) throws IOException { 1747 if (theIdDt != null && !theIdDt.isEmpty()) { 1748 write(theEventWriter, theElementName, theIdDt.getValueAsString()); 1749 } else { 1750 theEventWriter.writeNull(theElementName); 1751 } 1752 } 1753 1754 private void writeTagWithTextNode(JsonLikeWriter theEventWriter, String theElementName, StringDt theStringDt) throws IOException { 1755 if (StringUtils.isNotBlank(theStringDt.getValue())) { 1756 write(theEventWriter, theElementName, theStringDt.getValue()); 1757 } 1758 // else { 1759 // theEventWriter.writeNull(theElementName); 1760 // } 1761 } 1762 1763 public static Gson newGson() { 1764 Gson gson = new GsonBuilder().disableHtmlEscaping().create(); 1765 return gson; 1766 } 1767 1768 private static void write(JsonLikeWriter theWriter, String theName, String theValue) throws IOException { 1769 theWriter.write(theName, theValue); 1770 } 1771 1772 private class HeldExtension implements Comparable<HeldExtension> { 1773 1774 private CompositeChildElement myChildElem; 1775 private RuntimeChildDeclaredExtensionDefinition myDef; 1776 private boolean myModifier; 1777 private IBaseExtension<?, ?> myUndeclaredExtension; 1778 private IBase myValue; 1779 private CompositeChildElement myParent; 1780 1781 public HeldExtension(IBaseExtension<?, ?> theUndeclaredExtension, boolean theModifier, CompositeChildElement theChildElem, CompositeChildElement theParent) { 1782 assert theUndeclaredExtension != null; 1783 myUndeclaredExtension = theUndeclaredExtension; 1784 myModifier = theModifier; 1785 myChildElem = theChildElem; 1786 myParent = theParent; 1787 } 1788 1789 public HeldExtension(RuntimeChildDeclaredExtensionDefinition theDef, IBase theValue, CompositeChildElement theChildElem) { 1790 assert theDef != null; 1791 assert theValue != null; 1792 myDef = theDef; 1793 myValue = theValue; 1794 myChildElem = theChildElem; 1795 } 1796 1797 @Override 1798 public int compareTo(HeldExtension theArg0) { 1799 String url1 = myDef != null ? myDef.getExtensionUrl() : myUndeclaredExtension.getUrl(); 1800 String url2 = theArg0.myDef != null ? theArg0.myDef.getExtensionUrl() : theArg0.myUndeclaredExtension.getUrl(); 1801 url1 = defaultString(getExtensionUrl(url1)); 1802 url2 = defaultString(getExtensionUrl(url2)); 1803 return url1.compareTo(url2); 1804 } 1805 1806 public void write(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonLikeWriter theEventWriter) throws IOException { 1807 if (myUndeclaredExtension != null) { 1808 writeUndeclaredExtension(theResDef, theResource, theEventWriter, myUndeclaredExtension); 1809 } else { 1810 theEventWriter.beginObject(); 1811 1812 writeCommentsPreAndPost(myValue, theEventWriter); 1813 1814 JsonParser.write(theEventWriter, "url", getExtensionUrl(myDef.getExtensionUrl())); 1815 1816 /* 1817 * This makes sure that even if the extension contains a reference to a contained 1818 * resource which has a HAPI-assigned ID we'll still encode that ID. 1819 * 1820 * See #327 1821 */ 1822 List<? extends IBase> preProcessedValue = preProcessValues(myDef, theResource, Collections.singletonList(myValue), myChildElem); 1823 1824// // Check for undeclared extensions on the declared extension 1825// // (grrrrrr....) 1826// if (myValue instanceof ISupportsUndeclaredExtensions) { 1827// ISupportsUndeclaredExtensions value = (ISupportsUndeclaredExtensions)myValue; 1828// List<ExtensionDt> exts = value.getUndeclaredExtensions(); 1829// if (exts.size() > 0) { 1830// ArrayList<IBase> newValueList = new ArrayList<IBase>(); 1831// newValueList.addAll(preProcessedValue); 1832// newValueList.addAll(exts); 1833// preProcessedValue = newValueList; 1834// } 1835// } 1836 1837 myValue = preProcessedValue.get(0); 1838 1839 BaseRuntimeElementDefinition<?> def = myDef.getChildElementDefinitionByDatatype(myValue.getClass()); 1840 if (def.getChildType() == ChildTypeEnum.RESOURCE_BLOCK) { 1841 extractAndWriteExtensionsAsDirectChild(myValue, theEventWriter, def, theResDef, theResource, myChildElem, null); 1842 } else { 1843 String childName = myDef.getChildNameByDatatype(myValue.getClass()); 1844 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName, false, myParent, false); 1845 } 1846 1847 theEventWriter.endObject(); 1848 } 1849 } 1850 1851 1852 private void writeUndeclaredExtension(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonLikeWriter theEventWriter, IBaseExtension<?, ?> ext) throws IOException { 1853 IBase value = ext.getValue(); 1854 final String extensionUrl = getExtensionUrl(ext.getUrl()); 1855 1856 theEventWriter.beginObject(); 1857 1858 writeCommentsPreAndPost(myUndeclaredExtension, theEventWriter); 1859 1860 String elementId = getCompositeElementId(ext); 1861 if (isNotBlank(elementId)) { 1862 JsonParser.write(theEventWriter, "id", getCompositeElementId(ext)); 1863 } 1864 1865 JsonParser.write(theEventWriter, "url", extensionUrl); 1866 1867 boolean noValue = value == null || value.isEmpty(); 1868 if (noValue && ext.getExtension().isEmpty()) { 1869 ourLog.debug("Extension with URL[{}] has no value", extensionUrl); 1870 } else if (noValue) { 1871 1872 if (myModifier) { 1873 beginArray(theEventWriter, "modifierExtension"); 1874 } else { 1875 beginArray(theEventWriter, "extension"); 1876 } 1877 1878 for (Object next : ext.getExtension()) { 1879 writeUndeclaredExtension(theResDef, theResource, theEventWriter, (IBaseExtension<?, ?>) next); 1880 } 1881 theEventWriter.endArray(); 1882 } else { 1883 1884 /* 1885 * Pre-process value - This is called in case the value is a reference 1886 * since we might modify the text 1887 */ 1888 value = JsonParser.super.preProcessValues(myDef, theResource, Collections.singletonList(value), myChildElem).get(0); 1889 1890 RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition(); 1891 String childName = extDef.getChildNameByDatatype(value.getClass()); 1892 if (childName == null) { 1893 childName = "value" + WordUtils.capitalize(myContext.getElementDefinition(value.getClass()).getName()); 1894 } 1895 BaseRuntimeElementDefinition<?> childDef = extDef.getChildElementDefinitionByDatatype(value.getClass()); 1896 if (childDef == null) { 1897 throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + value.getClass().getCanonicalName()); 1898 } 1899 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName, true, myParent, false); 1900 } 1901 1902 // theEventWriter.name(myUndeclaredExtension.get); 1903 1904 theEventWriter.endObject(); 1905 } 1906 1907 } 1908 1909}