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