1 package ca.uhn.fhir.parser;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 import static org.apache.commons.lang3.StringUtils.*;
24
25 import java.io.IOException;
26 import java.io.Reader;
27 import java.io.StringReader;
28 import java.io.Writer;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Map.Entry;
37 import java.util.Set;
38
39 import javax.json.Json;
40 import javax.json.JsonArray;
41 import javax.json.JsonObject;
42 import javax.json.JsonReader;
43 import javax.json.JsonString;
44 import javax.json.JsonValue;
45 import javax.json.JsonValue.ValueType;
46 import javax.json.stream.JsonGenerator;
47 import javax.json.stream.JsonGeneratorFactory;
48 import javax.json.stream.JsonParsingException;
49
50 import org.apache.commons.lang3.StringUtils;
51 import org.apache.commons.lang3.Validate;
52
53 import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
54 import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
55 import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
56 import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
57 import ca.uhn.fhir.context.ConfigurationException;
58 import ca.uhn.fhir.context.FhirContext;
59 import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition;
60 import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition;
61 import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition;
62 import ca.uhn.fhir.context.RuntimeResourceDefinition;
63 import ca.uhn.fhir.model.api.BaseBundle;
64 import ca.uhn.fhir.model.api.Bundle;
65 import ca.uhn.fhir.model.api.BundleEntry;
66 import ca.uhn.fhir.model.api.ExtensionDt;
67 import ca.uhn.fhir.model.api.IElement;
68 import ca.uhn.fhir.model.api.IIdentifiableElement;
69 import ca.uhn.fhir.model.api.IPrimitiveDatatype;
70 import ca.uhn.fhir.model.api.IResource;
71 import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
72 import ca.uhn.fhir.model.api.Tag;
73 import ca.uhn.fhir.model.api.TagList;
74 import ca.uhn.fhir.model.api.annotation.Child;
75 import ca.uhn.fhir.model.dstu.composite.ContainedDt;
76 import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
77 import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
78 import ca.uhn.fhir.model.dstu.resource.Binary;
79 import ca.uhn.fhir.model.primitive.BooleanDt;
80 import ca.uhn.fhir.model.primitive.DecimalDt;
81 import ca.uhn.fhir.model.primitive.IdDt;
82 import ca.uhn.fhir.model.primitive.IntegerDt;
83 import ca.uhn.fhir.model.primitive.StringDt;
84 import ca.uhn.fhir.model.primitive.XhtmlDt;
85 import ca.uhn.fhir.narrative.INarrativeGenerator;
86
87 public class JsonParser extends BaseParser implements IParser {
88
89 private static final Set<String> BUNDLE_TEXTNODE_CHILDREN;
90 private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParser.HeldExtension.class);
91
92 static {
93 HashSet<String> hashSet = new HashSet<String>();
94 hashSet.add("title");
95 hashSet.add("id");
96 hashSet.add("updated");
97 hashSet.add("published");
98 BUNDLE_TEXTNODE_CHILDREN = Collections.unmodifiableSet(hashSet);
99 }
100
101 private FhirContext myContext;
102
103 private boolean myPrettyPrint;
104
105 public JsonParser(FhirContext theContext) {
106 super(theContext);
107 myContext = theContext;
108 }
109
110 private void addToHeldExtensions(int valueIdx, List<ExtensionDt> ext, ArrayList<ArrayList<HeldExtension>> list) {
111 if (ext.size() > 0) {
112 list.ensureCapacity(valueIdx);
113 while (list.size() <= valueIdx) {
114 list.add(null);
115 }
116 if (list.get(valueIdx) == null) {
117 list.set(valueIdx, new ArrayList<JsonParser.HeldExtension>());
118 }
119 for (ExtensionDt next : ext) {
120 list.get(valueIdx).add(new HeldExtension(next));
121 }
122 }
123 }
124
125 private void assertObjectOfType(JsonValue theResourceTypeObj, ValueType theValueType, String thePosition) {
126 if (theResourceTypeObj == null) {
127 throw new DataFormatException("Invalid JSON content detected, missing required element: '" + thePosition + "'");
128 }
129
130 if (theResourceTypeObj.getValueType() != theValueType) {
131 throw new DataFormatException("Invalid content of element " + thePosition + ", expected " + theValueType);
132 }
133 }
134
135 private JsonGenerator createJsonGenerator(Writer theWriter) {
136 Map<String, Object> properties = new HashMap<String, Object>(1);
137 if (myPrettyPrint) {
138 properties.put(JsonGenerator.PRETTY_PRINTING, myPrettyPrint);
139 }
140 JsonGeneratorFactory jgf = Json.createGeneratorFactory(properties);
141 JsonGenerator eventWriter = jgf.createGenerator(theWriter);
142 return eventWriter;
143 }
144
145 @Override
146 public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException {
147 JsonGenerator eventWriter = createJsonGenerator(theWriter);
148 eventWriter.writeStartObject();
149
150 eventWriter.write("resourceType", "Bundle");
151
152 writeTagWithTextNode(eventWriter, "title", theBundle.getTitle());
153 writeTagWithTextNode(eventWriter, "id", theBundle.getBundleId());
154 writeOptionalTagWithTextNode(eventWriter, "updated", theBundle.getUpdated());
155 writeOptionalTagWithTextNode(eventWriter, "published", theBundle.getPublished());
156
157 boolean linkStarted = false;
158 linkStarted = writeAtomLink(eventWriter, "self", theBundle.getLinkSelf(), linkStarted);
159 linkStarted = writeAtomLink(eventWriter, "first", theBundle.getLinkFirst(), linkStarted);
160 linkStarted = writeAtomLink(eventWriter, "previous", theBundle.getLinkPrevious(), linkStarted);
161 linkStarted = writeAtomLink(eventWriter, "next", theBundle.getLinkNext(), linkStarted);
162 linkStarted = writeAtomLink(eventWriter, "last", theBundle.getLinkLast(), linkStarted);
163 linkStarted = writeAtomLink(eventWriter, "fhir-base", theBundle.getLinkBase(), linkStarted);
164 if (linkStarted) {
165 eventWriter.writeEnd();
166 }
167
168 writeCategories(eventWriter, theBundle.getCategories());
169
170 writeOptionalTagWithTextNode(eventWriter, "totalResults", theBundle.getTotalResults());
171
172 writeAuthor(theBundle, eventWriter);
173
174 eventWriter.writeStartArray("entry");
175 for (BundleEntry nextEntry : theBundle.getEntries()) {
176 eventWriter.writeStartObject();
177
178 boolean deleted = nextEntry.getDeletedAt() != null && nextEntry.getDeletedAt().isEmpty() == false;
179 if (deleted) {
180 writeTagWithTextNode(eventWriter, "deleted", nextEntry.getDeletedAt());
181 }
182 writeTagWithTextNode(eventWriter, "title", nextEntry.getTitle());
183 writeTagWithTextNode(eventWriter, "id", nextEntry.getId());
184
185 linkStarted = false;
186 linkStarted = writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf(), linkStarted);
187 linkStarted = writeAtomLink(eventWriter, "alternate", nextEntry.getLinkAlternate(), linkStarted);
188 linkStarted = writeAtomLink(eventWriter, "search", nextEntry.getLinkSearch(), linkStarted);
189 if (linkStarted) {
190 eventWriter.writeEnd();
191 }
192
193 writeOptionalTagWithTextNode(eventWriter, "updated", nextEntry.getUpdated());
194 writeOptionalTagWithTextNode(eventWriter, "published", nextEntry.getPublished());
195
196 writeCategories(eventWriter, nextEntry.getCategories());
197
198 writeAuthor(nextEntry, eventWriter);
199
200 IResource resource = nextEntry.getResource();
201 if (resource != null && !resource.isEmpty() && !deleted) {
202 RuntimeResourceDefinition resDef = myContext.getResourceDefinition(resource);
203 encodeResourceToJsonStreamWriter(resDef, resource, eventWriter, "content", false);
204 }
205
206 if (nextEntry.getSummary().isEmpty()==false) {
207 eventWriter.write("summary", nextEntry.getSummary().getValueAsString());
208 }
209
210 eventWriter.writeEnd();
211 }
212 eventWriter.writeEnd();
213
214 eventWriter.writeEnd();
215 eventWriter.flush();
216 }
217
218 private void writeCategories(JsonGenerator eventWriter, TagList categories) {
219 if (categories != null && categories.size() > 0) {
220 eventWriter.writeStartArray("category");
221 for (Tag next : categories) {
222 eventWriter.writeStartObject();
223 eventWriter.write("term", defaultString(next.getTerm()));
224 eventWriter.write("label", defaultString(next.getLabel()));
225 eventWriter.write("scheme", defaultString(next.getScheme()));
226 eventWriter.writeEnd();
227 }
228 eventWriter.writeEnd();
229 }
230 }
231
232 private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition<?> theChildDef,
233 String theChildName, boolean theIsSubElementWithinResource) throws IOException {
234
235 switch (theChildDef.getChildType()) {
236 case PRIMITIVE_DATATYPE: {
237 IPrimitiveDatatype<?> value = (IPrimitiveDatatype<?>) theValue;
238 if (isBlank(value.getValueAsString())) {
239 break;
240 }
241
242 if (value instanceof IntegerDt) {
243 if (theChildName != null) {
244 theWriter.write(theChildName, ((IntegerDt) value).getValue());
245 } else {
246 theWriter.write(((IntegerDt) value).getValue());
247 }
248 } else if (value instanceof DecimalDt) {
249 if (theChildName != null) {
250 theWriter.write(theChildName, ((DecimalDt) value).getValue());
251 } else {
252 theWriter.write(((DecimalDt) value).getValue());
253 }
254 } else if (value instanceof BooleanDt) {
255 if (theChildName != null) {
256 theWriter.write(theChildName, ((BooleanDt) value).getValue());
257 } else {
258 theWriter.write(((BooleanDt) value).getValue());
259 }
260 } else {
261 String valueStr = value.getValueAsString();
262 if (theChildName != null) {
263 theWriter.write(theChildName, valueStr);
264 } else {
265 theWriter.write(valueStr);
266 }
267 }
268 break;
269 }
270 case RESOURCE_BLOCK:
271 case COMPOSITE_DATATYPE: {
272 BaseRuntimeElementCompositeDefinition<?> childCompositeDef = (BaseRuntimeElementCompositeDefinition<?>) theChildDef;
273 if (theChildName != null) {
274 theWriter.writeStartObject(theChildName);
275 } else {
276 theWriter.writeStartObject();
277 }
278 if (theValue instanceof ExtensionDt) {
279 theWriter.write("url", ((ExtensionDt) theValue).getUrlAsString());
280 }
281 encodeCompositeElementToStreamWriter(theResDef, theResource, theValue, theWriter, childCompositeDef, theIsSubElementWithinResource);
282 theWriter.writeEnd();
283 break;
284 }
285 case RESOURCE_REF: {
286 ResourceReferenceDt referenceDt = (ResourceReferenceDt) theValue;
287 if (theChildName != null) {
288 theWriter.writeStartObject(theChildName);
289 } else {
290 theWriter.writeStartObject();
291 }
292
293 String reference = determineReferenceText(referenceDt);
294
295 if (StringUtils.isNotBlank(reference)) {
296 theWriter.write(XmlParser.RESREF_REFERENCE, reference);
297 }
298 if (referenceDt.getDisplay().isEmpty() == false) {
299 theWriter.write(XmlParser.RESREF_DISPLAY, referenceDt.getDisplay().getValueAsString());
300 }
301 theWriter.writeEnd();
302 break;
303 }
304 case CONTAINED_RESOURCES: {
305 theWriter.writeStartArray(theChildName);
306 ContainedDt value = (ContainedDt) theValue;
307 for (IResource next : value.getContainedResources()) {
308 if (getContainedResources().getResourceId(next)!=null) {
309 continue;
310 }
311 encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true, fixContainedResourceId(next.getId().getValue()));
312 }
313 for (IResource next : getContainedResources().getContainedResources()) {
314 IdDt resourceId = getContainedResources().getResourceId(next);
315 encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true, fixContainedResourceId(resourceId.getValue()));
316 }
317 theWriter.writeEnd();
318 break;
319 }
320 case PRIMITIVE_XHTML: {
321 if (!getSuppressNarratives()) {
322 XhtmlDt dt = (XhtmlDt) theValue;
323 if (theChildName != null) {
324 theWriter.write(theChildName, dt.getValueAsString());
325 } else {
326 theWriter.write(dt.getValueAsString());
327 }
328 } else {
329 if (theChildName != null) {
330
331 } else {
332 theWriter.writeNull();
333 }
334 }
335 break;
336 }
337 case UNDECL_EXT:
338 default:
339 throw new IllegalStateException("Should not have this state here: " + theChildDef.getChildType().name());
340 }
341
342 }
343
344 private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter,
345 List<? extends BaseRuntimeChildDefinition> theChildren, boolean theIsSubElementWithinResource) throws IOException {
346 for (BaseRuntimeChildDefinition nextChild : theChildren) {
347 if (nextChild instanceof RuntimeChildNarrativeDefinition) {
348 INarrativeGenerator gen = myContext.getNarrativeGenerator();
349 if (gen != null) {
350 NarrativeDt narr = gen.generateNarrative(theResDef.getResourceProfile(), theResource);
351 if (narr != null) {
352 RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
353 String childName = nextChild.getChildNameByDatatype(child.getDatatype());
354 BaseRuntimeElementDefinition<?> type = child.getChildByName(childName);
355 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, narr, type, childName, theIsSubElementWithinResource);
356 continue;
357 }
358 }
359 }
360
361 List<? extends IElement> values = nextChild.getAccessor().getValues(theElement);
362 if (values == null || values.isEmpty()) {
363 continue;
364 }
365
366 String currentChildName = null;
367 boolean inArray = false;
368
369 ArrayList<ArrayList<HeldExtension>> extensions = new ArrayList<ArrayList<HeldExtension>>(0);
370 ArrayList<ArrayList<HeldExtension>> modifierExtensions = new ArrayList<ArrayList<HeldExtension>>(0);
371
372 int valueIdx = 0;
373 for (IElement nextValue : values) {
374 if (nextValue == null || nextValue.isEmpty()) {
375 if (nextValue instanceof ContainedDt) {
376 if (theIsSubElementWithinResource || getContainedResources().isEmpty()) {
377 continue;
378 }
379 } else {
380 continue;
381 }
382 }
383
384 Class<? extends IElement> type = nextValue.getClass();
385 String childName = nextChild.getChildNameByDatatype(type);
386 BaseRuntimeElementDefinition<?> childDef = nextChild.getChildElementDefinitionByDatatype(type);
387 if (childDef == null) {
388 super.throwExceptionForUnknownChildType(nextChild, type);
389 }
390 boolean primitive = childDef.getChildType() == ChildTypeEnum.PRIMITIVE_DATATYPE;
391
392 if (childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCES && theIsSubElementWithinResource) {
393 continue;
394 }
395
396 if (nextChild instanceof RuntimeChildDeclaredExtensionDefinition) {
397
398
399
400
401
402
403
404 } else {
405
406 if (currentChildName == null || !currentChildName.equals(childName)) {
407 if (inArray) {
408 theEventWriter.writeEnd();
409 }
410 if (nextChild.getMax() > 1 || nextChild.getMax() == Child.MAX_UNLIMITED) {
411 theEventWriter.writeStartArray(childName);
412 inArray = true;
413 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theIsSubElementWithinResource);
414 } else if (nextChild instanceof RuntimeChildNarrativeDefinition && theIsSubElementWithinResource) {
415
416 } else {
417 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, childName, theIsSubElementWithinResource);
418 }
419 currentChildName = childName;
420 } else {
421 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theIsSubElementWithinResource);
422 }
423
424 if (nextValue instanceof ISupportsUndeclaredExtensions && primitive) {
425 List<ExtensionDt> ext = ((ISupportsUndeclaredExtensions) nextValue).getUndeclaredExtensions();
426 addToHeldExtensions(valueIdx, ext, extensions);
427
428 ext = ((ISupportsUndeclaredExtensions) nextValue).getUndeclaredModifierExtensions();
429 addToHeldExtensions(valueIdx, ext, modifierExtensions);
430 }
431
432 }
433
434 valueIdx++;
435 }
436
437 if (inArray) {
438 theEventWriter.writeEnd();
439 }
440
441 if (extensions.size() > 0 || modifierExtensions.size() > 0) {
442 theEventWriter.writeStartArray('_' + currentChildName);
443
444 for (int i = 0; i < valueIdx; i++) {
445 boolean haveContent = false;
446 if (extensions.size() > i && extensions.get(i) != null && extensions.get(i).isEmpty() == false) {
447 haveContent = true;
448 theEventWriter.writeStartObject();
449 theEventWriter.writeStartArray("extension");
450 for (HeldExtension nextExt : extensions.get(i)) {
451 nextExt.write(theResDef, theResource, theEventWriter);
452 }
453 theEventWriter.writeEnd();
454 theEventWriter.writeEnd();
455 }
456
457 if (!haveContent) {
458
459 theEventWriter.writeNull();
460 }
461 }
462
463 theEventWriter.writeEnd();
464 }
465 }
466 }
467
468 private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter,
469 BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIsSubElementWithinResource) throws IOException, DataFormatException {
470 extractAndWriteExtensionsAsDirectChild(theElement, theEventWriter, resDef, theResDef, theResource);
471 encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getExtensions(), theIsSubElementWithinResource);
472 encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren(),theIsSubElementWithinResource);
473 }
474
475 private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull,
476 boolean theIsSubElementWithinResource) throws IOException {
477 String resourceId = null;
478 if (theIsSubElementWithinResource && StringUtils.isNotBlank(theResource.getId().getValue())) {
479 resourceId = theResource.getId().getValue();
480 }
481
482 encodeResourceToJsonStreamWriter(theResDef, theResource, theEventWriter, theObjectNameOrNull, theIsSubElementWithinResource, resourceId);
483 }
484
485 private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theIsSubElementWithinResource,
486 String theResourceId) throws IOException {
487 if (!theIsSubElementWithinResource) {
488 super.containResourcesForEncoding(theResource);
489 }
490
491 RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
492
493 if (theObjectNameOrNull == null) {
494 theEventWriter.writeStartObject();
495 } else {
496 theEventWriter.writeStartObject(theObjectNameOrNull);
497 }
498
499 theEventWriter.write("resourceType", resDef.getName());
500 if (theResourceId != null) {
501 theEventWriter.write("id", theResourceId);
502 }
503
504 if (theResource instanceof Binary) {
505 Binary bin = (Binary) theResource;
506 theEventWriter.write("contentType", bin.getContentType());
507 theEventWriter.write("content", bin.getContentAsBase64());
508 } else {
509 encodeCompositeElementToStreamWriter(theResDef, theResource, theResource, theEventWriter, resDef, theIsSubElementWithinResource);
510 }
511 theEventWriter.writeEnd();
512 }
513
514 @Override
515 public void encodeResourceToWriter(IResource theResource, Writer theWriter) throws IOException {
516 Validate.notNull(theResource, "Resource can not be null");
517
518 JsonGenerator eventWriter = createJsonGenerator(theWriter);
519
520 RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
521 encodeResourceToJsonStreamWriter(resDef, theResource, eventWriter, null, false);
522 eventWriter.flush();
523 }
524
525 @Override
526 public void encodeTagListToWriter(TagList theTagList, Writer theWriter) throws IOException {
527 JsonGenerator eventWriter = createJsonGenerator(theWriter);
528
529 eventWriter.writeStartObject();
530
531 eventWriter.write("resourceType", TagList.ELEMENT_NAME);
532
533 eventWriter.writeStartArray(TagList.ATTR_CATEGORY);
534 for (Tag next : theTagList) {
535 eventWriter.writeStartObject();
536
537 if (isNotBlank(next.getTerm())) {
538 eventWriter.write(Tag.ATTR_TERM, next.getTerm());
539 }
540 if (isNotBlank(next.getLabel())) {
541 eventWriter.write(Tag.ATTR_LABEL, next.getLabel());
542 }
543 if (isNotBlank(next.getScheme())) {
544 eventWriter.write(Tag.ATTR_SCHEME, next.getScheme());
545 }
546
547 eventWriter.writeEnd();
548 }
549 eventWriter.writeEnd();
550
551 eventWriter.writeEnd();
552 eventWriter.flush();
553 }
554
555
556
557
558 private void extractAndWriteExtensionsAsDirectChild(IElement theElement, JsonGenerator theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef,
559 IResource theResource) throws IOException {
560 List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
561 List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
562
563
564 extractUndeclaredExtensions(theElement, extensions, modifierExtensions);
565
566
567 extractDeclaredExtensions(theElement, theElementDef, extensions, modifierExtensions);
568
569
570 writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, extensions, modifierExtensions);
571 }
572
573 private void extractDeclaredExtensions(IElement theResource, BaseRuntimeElementDefinition<?> resDef, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions) {
574 for (RuntimeChildDeclaredExtensionDefinition nextDef : resDef.getExtensionsNonModifier()) {
575 for (IElement nextValue : nextDef.getAccessor().getValues(theResource)) {
576 if (nextValue != null) {
577 if (nextValue == null || nextValue.isEmpty()) {
578 continue;
579 }
580 extensions.add(new HeldExtension(nextDef, nextValue));
581 }
582 }
583 }
584 for (RuntimeChildDeclaredExtensionDefinition nextDef : resDef.getExtensionsModifier()) {
585 for (IElement nextValue : nextDef.getAccessor().getValues(theResource)) {
586 if (nextValue != null) {
587 if (nextValue == null || nextValue.isEmpty()) {
588 continue;
589 }
590 modifierExtensions.add(new HeldExtension(nextDef, nextValue));
591 }
592 }
593 }
594 }
595
596 private void extractUndeclaredExtensions(IElement theResource, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions) {
597 if (theResource instanceof ISupportsUndeclaredExtensions) {
598 List<ExtensionDt> ext = ((ISupportsUndeclaredExtensions) theResource).getUndeclaredExtensions();
599 for (ExtensionDt next : ext) {
600 if (next == null || next.isEmpty()) {
601 continue;
602 }
603 extensions.add(new HeldExtension(next));
604 }
605
606 ext = ((ISupportsUndeclaredExtensions) theResource).getUndeclaredModifierExtensions();
607 for (ExtensionDt next : ext) {
608 if (next == null || next.isEmpty()) {
609 continue;
610 }
611 modifierExtensions.add(new HeldExtension(next));
612 }
613 }
614 }
615
616 private void parseAlternates(JsonValue theAlternateVal, ParserState<?> theState) {
617 if (theAlternateVal == null || theAlternateVal.getValueType() == ValueType.NULL) {
618 return;
619 }
620 JsonObject alternate = (JsonObject) theAlternateVal;
621 for (Entry<String, JsonValue> nextEntry : alternate.entrySet()) {
622 String nextKey = nextEntry.getKey();
623 JsonValue nextVal = nextEntry.getValue();
624 if ("extension".equals(nextKey)) {
625 boolean isModifier = false;
626 JsonArray array = (JsonArray) nextEntry.getValue();
627 parseExtension(theState, array, isModifier);
628 } else if ("modifierExtension".equals(nextKey)) {
629 boolean isModifier = true;
630 JsonArray array = (JsonArray) nextEntry.getValue();
631 parseExtension(theState, array, isModifier);
632 } else if ("id".equals(nextKey)) {
633 switch (nextVal.getValueType()) {
634 case STRING:
635 theState.attributeValue("id", ((JsonString) nextVal).getString());
636 break;
637 case NULL:
638 break;
639 default:
640 break;
641 }
642 }
643 }
644 }
645
646 @Override
647 public <T extends IResource> Bundle parseBundle(Class<T> theResourceType, Reader theReader) {
648 JsonReader reader;
649 JsonObject object;
650
651 try {
652 reader = Json.createReader(theReader);
653 object = reader.readObject();
654 } catch (JsonParsingException e) {
655 if (e.getMessage().startsWith("Unexpected char 39")) {
656 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);
657 }
658 throw new DataFormatException("Failed to parse JSON encoded FHIR content: " + e.getMessage(), e);
659 }
660 JsonValue resourceTypeObj = object.get("resourceType");
661 assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
662 String resourceType = ((JsonString) resourceTypeObj).getString();
663 if (!"Bundle".equals(resourceType)) {
664 throw new DataFormatException("Trying to parse bundle but found resourceType other than 'Bundle'. Found: '" + resourceType + "'");
665 }
666
667 ParserState<Bundle> state = ParserState.getPreAtomInstance(myContext, theResourceType, true);
668 state.enteringNewElement(null, "feed");
669
670 parseBundleChildren(object, state);
671
672 state.endingElement();
673
674 Bundle retVal = state.getObject();
675
676 return retVal;
677 }
678
679 private void parseBundleChildren(JsonObject theObject, ParserState<?> theState) {
680 for (String nextName : theObject.keySet()) {
681 if ("resourceType".equals(nextName)) {
682 continue;
683 } else if ("link".equals(nextName)) {
684 JsonArray entries = theObject.getJsonArray(nextName);
685 for (JsonValue jsonValue : entries) {
686 theState.enteringNewElement(null, "link");
687 JsonObject linkObj = (JsonObject) jsonValue;
688 String rel = linkObj.getString("rel", null);
689 String href = linkObj.getString("href", null);
690 theState.attributeValue("rel", rel);
691 theState.attributeValue("href", href);
692 theState.endingElement();
693 }
694 continue;
695 } else if ("entry".equals(nextName)) {
696 JsonArray entries = theObject.getJsonArray(nextName);
697 for (JsonValue jsonValue : entries) {
698 theState.enteringNewElement(null, "entry");
699 parseBundleChildren((JsonObject) jsonValue, theState);
700 theState.endingElement();
701 }
702 continue;
703 } else if (BUNDLE_TEXTNODE_CHILDREN.contains(nextName)) {
704 theState.enteringNewElement(null, nextName);
705 theState.string(theObject.getString(nextName, null));
706 theState.endingElement();
707 continue;
708 }
709
710 JsonValue nextVal = theObject.get(nextName);
711 parseChildren(theState, nextName, nextVal, null);
712
713 }
714 }
715
716 private void parseChildren(JsonObject theObject, ParserState<?> theState) {
717 String elementId = null;
718 for (String nextName : theObject.keySet()) {
719 if ("resourceType".equals(nextName)) {
720 continue;
721 } else if ("id".equals(nextName)) {
722 elementId = theObject.getString(nextName);
723 continue;
724 } else if ("_id".equals(nextName)) {
725
726 elementId = theObject.getString(nextName);
727 continue;
728 } else if ("extension".equals(nextName)) {
729 JsonArray array = theObject.getJsonArray(nextName);
730 parseExtension(theState, array, false);
731 continue;
732 } else if ("modifierExtension".equals(nextName)) {
733 JsonArray array = theObject.getJsonArray(nextName);
734 parseExtension(theState, array, true);
735 continue;
736 } else if (nextName.charAt(0) == '_') {
737 continue;
738 }
739
740 JsonValue nextVal = theObject.get(nextName);
741 JsonValue alternateVal = theObject.get('_' + nextName);
742
743 parseChildren(theState, nextName, nextVal, alternateVal);
744
745 }
746
747 if (elementId != null) {
748 IElement object = (IElement) theState.getObject();
749 if (object instanceof IIdentifiableElement) {
750 ((IIdentifiableElement) object).setElementSpecificId(elementId);
751 } else if (object instanceof IResource) {
752 ((IResource) object).setId(new IdDt(elementId));
753 }
754 }
755 }
756
757 private void parseChildren(ParserState<?> theState, String theName, JsonValue theJsonVal, JsonValue theAlternateVal) {
758 switch (theJsonVal.getValueType()) {
759 case ARRAY: {
760 JsonArray nextArray = (JsonArray) theJsonVal;
761 JsonArray nextAlternateArray = (JsonArray) theAlternateVal;
762 for (int i = 0; i < nextArray.size(); i++) {
763 JsonValue nextObject = nextArray.get(i);
764 JsonValue nextAlternate = null;
765 if (nextAlternateArray != null) {
766 nextAlternate = nextAlternateArray.get(i);
767 }
768 parseChildren(theState, theName, nextObject, nextAlternate);
769 }
770 break;
771 }
772 case OBJECT: {
773 theState.enteringNewElement(null, theName);
774 parseAlternates(theAlternateVal, theState);
775 JsonObject nextObject = (JsonObject) theJsonVal;
776 boolean preResource = false;
777 if (theState.isPreResource()) {
778 String resType = nextObject.getString("resourceType");
779 if (isBlank(resType)) {
780 throw new DataFormatException("Missing 'resourceType' from resource");
781 }
782 theState.enteringNewElement(null, resType);
783 preResource = true;
784 }
785 parseChildren(nextObject, theState);
786 if (preResource) {
787 theState.endingElement();
788 }
789 theState.endingElement();
790 break;
791 }
792 case STRING: {
793 JsonString nextValStr = (JsonString) theJsonVal;
794 theState.enteringNewElement(null, theName);
795 theState.attributeValue("value", nextValStr.getString());
796 parseAlternates(theAlternateVal, theState);
797 theState.endingElement();
798 break;
799 }
800 case NUMBER:
801 case FALSE:
802 case TRUE:
803 theState.enteringNewElement(null, theName);
804 theState.attributeValue("value", theJsonVal.toString());
805 parseAlternates(theAlternateVal, theState);
806 theState.endingElement();
807 break;
808 case NULL:
809 break;
810 }
811 }
812
813 private void parseExtension(ParserState<?> theState, JsonArray array, boolean theIsModifier) {
814 for (int i = 0; i < array.size(); i++) {
815 JsonObject nextExtObj = array.getJsonObject(i);
816 String url = nextExtObj.getString("url");
817 theState.enteringNewElementExtension(null, url, theIsModifier);
818 for (Iterator<String> iter = nextExtObj.keySet().iterator(); iter.hasNext();) {
819 String next = iter.next();
820 if ("url".equals(next)) {
821 continue;
822 } else if ("extension".equals(next)) {
823 JsonArray jsonVal = (JsonArray) nextExtObj.get(next);
824 parseExtension(theState, jsonVal, false);
825 } else if ("modifierExtension".equals(next)) {
826 JsonArray jsonVal = (JsonArray) nextExtObj.get(next);
827 parseExtension(theState, jsonVal, true);
828 } else {
829 JsonValue jsonVal = nextExtObj.get(next);
830 parseChildren(theState, next, jsonVal, null);
831 }
832 }
833 theState.endingElement();
834 }
835 }
836
837 @Override
838 public <T extends IResource> T parseResource(Class<T> theResourceType, Reader theReader) {
839 JsonReader reader = Json.createReader(theReader);
840 JsonObject object = reader.readObject();
841
842 JsonValue resourceTypeObj = object.get("resourceType");
843 assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
844 String resourceType = ((JsonString) resourceTypeObj).getString();
845
846 RuntimeResourceDefinition def;
847 if (theResourceType != null) {
848 def = myContext.getResourceDefinition(theResourceType);
849 } else {
850 def = myContext.getResourceDefinition(resourceType);
851 }
852
853 ParserState<? extends IResource> state = ParserState.getPreResourceInstance(def.getImplementingClass(), myContext, true);
854 state.enteringNewElement(null, def.getName());
855
856 parseChildren(object, state);
857
858 state.endingElement();
859
860 @SuppressWarnings("unchecked")
861 T retVal = (T) state.getObject();
862
863 return retVal;
864 }
865
866 @Override
867 public <T extends IResource> T parseResource(Class<T> theResourceType, String theMessageString) {
868 return parseResource(theResourceType, new StringReader(theMessageString));
869 }
870
871 @Override
872 public TagList parseTagList(Reader theReader) {
873 JsonReader reader = Json.createReader(theReader);
874 JsonObject object = reader.readObject();
875
876 JsonValue resourceTypeObj = object.get("resourceType");
877 assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
878 String resourceType = ((JsonString) resourceTypeObj).getString();
879
880 ParserState<TagList> state = ParserState.getPreTagListInstance(myContext, true);
881 state.enteringNewElement(null, resourceType);
882
883 parseChildren(object, state);
884
885 state.endingElement();
886
887 return state.getObject();
888 }
889
890 @Override
891 public IParser setPrettyPrint(boolean thePrettyPrint) {
892 myPrettyPrint = thePrettyPrint;
893 return this;
894 }
895
896 private boolean writeAtomLink(JsonGenerator theEventWriter, String theRel, StringDt theLink, boolean theStarted) {
897 boolean retVal = theStarted;
898 if (isNotBlank(theLink.getValue())) {
899 if (theStarted == false) {
900 theEventWriter.writeStartArray("link");
901 retVal = true;
902 }
903
904 theEventWriter.writeStartObject();
905 theEventWriter.write("rel", theRel);
906 theEventWriter.write("href", theLink.getValue());
907 theEventWriter.writeEnd();
908 }
909 return retVal;
910 }
911
912 private void writeAuthor(BaseBundle theBundle, JsonGenerator eventWriter) {
913 if (StringUtils.isNotBlank(theBundle.getAuthorName().getValue())) {
914 eventWriter.writeStartArray("author");
915 eventWriter.writeStartObject();
916 writeTagWithTextNode(eventWriter, "name", theBundle.getAuthorName());
917 writeOptionalTagWithTextNode(eventWriter, "uri", theBundle.getAuthorUri());
918 eventWriter.writeEnd();
919 eventWriter.writeEnd();
920 }
921 }
922
923 private void writeExtensionsAsDirectChild(IResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions,
924 List<HeldExtension> modifierExtensions) throws IOException {
925 if (extensions.isEmpty() == false) {
926 theEventWriter.writeStartArray("extension");
927 for (HeldExtension next : extensions) {
928 next.write(resDef, theResource, theEventWriter);
929 }
930 theEventWriter.writeEnd();
931 }
932 if (modifierExtensions.isEmpty() == false) {
933 theEventWriter.writeStartArray("modifierExtension");
934 for (HeldExtension next : modifierExtensions) {
935 next.write(resDef, theResource, theEventWriter);
936 }
937 theEventWriter.writeEnd();
938 }
939 }
940
941 private void writeOptionalTagWithTextNode(JsonGenerator theEventWriter, String theElementName, IPrimitiveDatatype<?> theInstantDt) {
942 String str = theInstantDt.getValueAsString();
943 if (StringUtils.isNotBlank(str)) {
944 theEventWriter.write(theElementName, theInstantDt.getValueAsString());
945 }
946 }
947
948 private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, IPrimitiveDatatype<?> theIdDt) {
949 if (theIdDt != null && !theIdDt.isEmpty()) {
950 theEventWriter.write(theElementName, theIdDt.getValueAsString());
951 } else {
952 theEventWriter.writeNull(theElementName);
953 }
954 }
955
956 private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, StringDt theStringDt) {
957 if (StringUtils.isNotBlank(theStringDt.getValue())) {
958 theEventWriter.write(theElementName, theStringDt.getValue());
959 }
960
961
962
963 }
964
965 private class HeldExtension {
966
967 private RuntimeChildDeclaredExtensionDefinition myDef;
968 private ExtensionDt myUndeclaredExtension;
969 private IElement myValue;
970
971 public HeldExtension(ExtensionDt theUndeclaredExtension) {
972 assert theUndeclaredExtension != null;
973 myUndeclaredExtension = theUndeclaredExtension;
974 }
975
976 public HeldExtension(RuntimeChildDeclaredExtensionDefinition theDef, IElement theValue) {
977 assert theDef != null;
978 assert theValue != null;
979 myDef = theDef;
980 myValue = theValue;
981 }
982
983 public void write(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theEventWriter) throws IOException {
984 if (myUndeclaredExtension != null) {
985 writeUndeclaredExt(theResDef, theResource, theEventWriter, myUndeclaredExtension);
986 } else {
987 theEventWriter.writeStartObject();
988 theEventWriter.write("url", myDef.getExtensionUrl());
989
990 BaseRuntimeElementDefinition<?> def = myDef.getChildElementDefinitionByDatatype(myValue.getClass());
991 if (def.getChildType() == ChildTypeEnum.RESOURCE_BLOCK) {
992 extractAndWriteExtensionsAsDirectChild(myValue, theEventWriter, def, theResDef, theResource);
993 } else {
994
995
996
997 String childName = myDef.getChildNameByDatatype(myValue.getClass());
998 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName,false);
999 }
1000
1001
1002
1003 theEventWriter.writeEnd();
1004 }
1005 }
1006
1007 private void writeUndeclaredExt(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theEventWriter, ExtensionDt ext) throws IOException {
1008 IElement value = ext.getValue();
1009
1010 theEventWriter.writeStartObject();
1011 theEventWriter.write("url", ext.getUrl().getValue());
1012
1013 boolean noValue = value == null || value.isEmpty();
1014 if (noValue && ext.getAllUndeclaredExtensions().isEmpty()) {
1015 ourLog.debug("Extension with URL[{}] has no value", ext.getUrl().getValue());
1016 } else if (noValue) {
1017 theEventWriter.writeStartArray("extension");
1018 for (ExtensionDt next : ext.getUndeclaredExtensions()) {
1019 writeUndeclaredExt(theResDef, theResource, theEventWriter, next);
1020 }
1021 theEventWriter.writeEnd();
1022 } else {
1023 RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition();
1024 String childName = extDef.getChildNameByDatatype(value.getClass());
1025 if (childName == null) {
1026 throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + value.getClass().getCanonicalName());
1027 }
1028 BaseRuntimeElementDefinition<?> childDef = extDef.getChildElementDefinitionByDatatype(value.getClass());
1029 encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName,true);
1030 }
1031
1032
1033
1034 theEventWriter.writeEnd();
1035 }
1036
1037 }
1038 }