001/* 002 * #%L 003 * HAPI FHIR - Core Library 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.parser; 021 022import ca.uhn.fhir.context.FhirContext; 023import ca.uhn.fhir.i18n.Msg; 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.IBaseBundle; 028import org.hl7.fhir.instance.model.api.IBaseResource; 029 030import java.io.BufferedReader; 031import java.io.IOException; 032import java.io.Reader; 033import java.io.Writer; 034import java.util.List; 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) 073 throws IOException { 074 // We only encode bundles to NDJSON. 075 if (!(IBaseBundle.class.isAssignableFrom(theResource.getClass()))) { 076 throw new IllegalArgumentException(Msg.code(1833) + "NDJsonParser can only encode Bundle types. Received " 077 + 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) 098 throws DataFormatException { 099 // We can only parse to bundles. 100 if ((theResourceType != null) && (!(IBaseBundle.class.isAssignableFrom(theResourceType)))) { 101 throw new DataFormatException(Msg.code(1834) + "NDJsonParser can only parse to Bundle types. Received " 102 + theResourceType.getName()); 103 } 104 105 try { 106 // Now we go through line-by-line parsing the JSON and then stuffing it into a bundle. 107 BundleBuilder myBuilder = new BundleBuilder(myFhirContext); 108 myBuilder.setType("collection"); 109 BufferedReader myBufferedReader = new BufferedReader(theReader); 110 String jsonString = myBufferedReader.readLine(); 111 while (jsonString != null) { 112 // And add it to a collection in a Bundle. 113 // The string must be trimmed, as per the NDJson spec 3.2 114 myBuilder.addCollectionEntry(myJsonParser.parseResource(jsonString.trim())); 115 // Try to read another line. 116 jsonString = myBufferedReader.readLine(); 117 } 118 119 return (T) myBuilder.getBundle(); 120 } catch (IOException err) { 121 throw new DataFormatException(Msg.code(1835) + err.getMessage()); 122 } 123 } 124}