001package ca.uhn.fhir.parser; 002 003/* 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2022 Smile CDR, Inc. 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 ca.uhn.fhir.context.*; 024import ca.uhn.fhir.rest.api.EncodingEnum; 025import ca.uhn.fhir.util.BundleBuilder; 026import ca.uhn.fhir.util.BundleUtil; 027import org.hl7.fhir.instance.model.api.*; 028 029import java.io.IOException; 030import java.io.BufferedReader; 031import java.io.Reader; 032import java.io.Writer; 033import java.util.List; 034 035 036/** 037 * This class is the FHIR NDJSON parser/encoder. Users should not interact with this class directly, but should use 038 * {@link FhirContext#newNDJsonParser()} to get an instance. 039 */ 040public class NDJsonParser extends BaseParser { 041 042 private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(NDJsonParser.class); 043 044 private IParser myJsonParser; 045 private FhirContext myFhirContext; 046 047 /** 048 * Do not use this constructor, the recommended way to obtain a new instance of the NDJSON parser is to invoke 049 * {@link FhirContext#newNDJsonParser()}. 050 * 051 * @param theParserErrorHandler 052 */ 053 public NDJsonParser(FhirContext theContext, IParserErrorHandler theParserErrorHandler) { 054 super(theContext, theParserErrorHandler); 055 myFhirContext = theContext; 056 057 myJsonParser = theContext.newJsonParser(); 058 } 059 060 @Override 061 public IParser setPrettyPrint(boolean thePrettyPrint) { 062 myJsonParser.setPrettyPrint(thePrettyPrint); 063 return this; 064 } 065 066 @Override 067 public EncodingEnum getEncoding() { 068 return EncodingEnum.NDJSON; 069 } 070 071 @Override 072 protected void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter, EncodeContext theEncodeContext) throws IOException { 073 // We only encode bundles to NDJSON. 074 if (!(IBaseBundle.class.isAssignableFrom(theResource.getClass()))) { 075 throw new IllegalArgumentException("NDJsonParser can only encode Bundle types. Received " + theResource.getClass().getName()); 076 } 077 078 // Ok, convert the bundle to a list of resources. 079 List<IBaseResource> theBundleResources = BundleUtil.toListOfResources(myFhirContext, (IBaseBundle) theResource); 080 081 // Now we write each one in turn. 082 // Use newline only as a line separator, not at the end of the file. 083 boolean isFirstResource = true; 084 for (IBaseResource theBundleEntryResource : theBundleResources) { 085 if (!(isFirstResource)) { 086 theWriter.write("\n"); 087 } 088 isFirstResource = false; 089 090 myJsonParser.encodeResourceToWriter(theBundleEntryResource, theWriter); 091 } 092 } 093 094 @Override 095 public <T extends IBaseResource> T doParseResource(Class<T> theResourceType, Reader theReader) throws DataFormatException { 096 // We can only parse to bundles. 097 if ((theResourceType != null) && (!(IBaseBundle.class.isAssignableFrom(theResourceType)))) { 098 throw new DataFormatException("NDJsonParser can only parse to Bundle types. Received " + theResourceType.getName()); 099 } 100 101 try { 102 // Now we go through line-by-line parsing the JSON and then stuffing it into a bundle. 103 BundleBuilder myBuilder = new BundleBuilder(myFhirContext); 104 myBuilder.setType("collection"); 105 BufferedReader myBufferedReader = new BufferedReader(theReader); 106 String jsonString = myBufferedReader.readLine(); 107 while (jsonString != null) { 108 // And add it to a collection in a Bundle. 109 // The string must be trimmed, as per the NDJson spec 3.2 110 myBuilder.addCollectionEntry(myJsonParser.parseResource(jsonString.trim())); 111 // Try to read another line. 112 jsonString = myBufferedReader.readLine(); 113 } 114 115 return (T) myBuilder.getBundle(); 116 } catch (IOException err) { 117 throw new DataFormatException(err.getMessage()); 118 } 119 } 120}