001/* 002 * SPDX-FileCopyrightText: none 003 * SPDX-License-Identifier: CC0-1.0 004 */ 005 006package gov.nist.secauto.metaschema.databind.io.json; 007 008import com.fasterxml.jackson.core.JsonFactory; 009import com.fasterxml.jackson.core.JsonParser; 010 011import gov.nist.secauto.metaschema.core.configuration.IConfiguration; 012import gov.nist.secauto.metaschema.core.configuration.IMutableConfiguration; 013import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; 014import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItemFactory; 015import gov.nist.secauto.metaschema.core.model.IBoundObject; 016import gov.nist.secauto.metaschema.core.util.ObjectUtils; 017import gov.nist.secauto.metaschema.databind.io.AbstractDeserializer; 018import gov.nist.secauto.metaschema.databind.io.DeserializationFeature; 019import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelAssembly; 020 021import java.io.IOException; 022import java.io.Reader; 023import java.net.URI; 024 025import edu.umd.cs.findbugs.annotations.NonNull; 026import nl.talsmasoftware.lazy4j.Lazy; 027 028/** 029 * Provides support for reading JSON-based data based on a bound Metaschema 030 * module. 031 * 032 * @param <CLASS> 033 * the Java type of the bound object representing the root node to read 034 */ 035public class DefaultJsonDeserializer<CLASS extends IBoundObject> 036 extends AbstractDeserializer<CLASS> { 037 private Lazy<JsonFactory> factory; 038 039 /** 040 * Construct a new JSON deserializer that will parse the bound class identified 041 * by the {@code classBinding}. 042 * 043 * @param definition 044 * the bound class information for the Java type this deserializer is 045 * operating on 046 */ 047 public DefaultJsonDeserializer(@NonNull IBoundDefinitionModelAssembly definition) { 048 super(definition); 049 resetFactory(); 050 } 051 052 /** 053 * For use by subclasses to reset the underlying JSON factory when an important 054 * change has occurred that will change how the factory produces a 055 * {@link JsonParser}. 056 */ 057 protected final void resetFactory() { 058 this.factory = Lazy.lazy(this::newFactoryInstance); 059 } 060 061 @Override 062 protected void configurationChanged(IMutableConfiguration<DeserializationFeature<?>> config) { 063 super.configurationChanged(config); 064 resetFactory(); 065 } 066 067 /** 068 * Get a JSON factory instance. 069 * <p> 070 * This method can be used by sub-classes to create a customized factory 071 * instance. 072 * 073 * @return the factory 074 */ 075 @NonNull 076 protected JsonFactory newFactoryInstance() { 077 return JsonFactoryFactory.instance(); 078 } 079 080 /** 081 * Get the parser factory associated with this deserializer. 082 * 083 * @return the factory instance 084 */ 085 @NonNull 086 protected JsonFactory getJsonFactory() { 087 return ObjectUtils.notNull(factory.get()); 088 } 089 090 /** 091 * Using the managed JSON factory, create a new JSON parser instance using the 092 * provided reader. 093 * 094 * @param reader 095 * the reader for the parser to read data from 096 * @return the new parser 097 * @throws IOException 098 * if an error occurred while creating the parser 099 */ 100 @SuppressWarnings("resource") // reader resource not owned 101 @NonNull 102 protected final JsonParser newJsonParser(@NonNull Reader reader) throws IOException { 103 return ObjectUtils.notNull(getJsonFactory().createParser(reader)); 104 } 105 106 @Override 107 protected INodeItem deserializeToNodeItemInternal(@NonNull Reader reader, @NonNull URI documentUri) 108 throws IOException { 109 INodeItem retval; 110 try (JsonParser jsonParser = newJsonParser(reader)) { 111 MetaschemaJsonReader parser = new MetaschemaJsonReader(jsonParser, documentUri); 112 IBoundDefinitionModelAssembly definition = getDefinition(); 113 IConfiguration<DeserializationFeature<?>> configuration = getConfiguration(); 114 115 if (definition.isRoot() 116 && configuration.isFeatureEnabled(DeserializationFeature.DESERIALIZE_JSON_ROOT_PROPERTY)) { 117 // now parse the root property 118 CLASS value = ObjectUtils.requireNonNull(parser.readObjectRoot( 119 definition, 120 ObjectUtils.notNull(definition.getRootJsonName()))); 121 122 retval = INodeItemFactory.instance().newDocumentNodeItem(definition, documentUri, value); 123 } else { 124 // read the top-level definition 125 CLASS value = ObjectUtils.asType(parser.readObject(definition)); 126 127 retval = INodeItemFactory.instance().newAssemblyNodeItem(definition, documentUri, value); 128 } 129 return retval; 130 } 131 } 132 133 @Override 134 public CLASS deserializeToValueInternal(@NonNull Reader reader, @NonNull URI documentUri) throws IOException { 135 try (JsonParser jsonParser = newJsonParser(reader)) { 136 MetaschemaJsonReader parser = new MetaschemaJsonReader(jsonParser, documentUri); 137 IBoundDefinitionModelAssembly definition = getDefinition(); 138 IConfiguration<DeserializationFeature<?>> configuration = getConfiguration(); 139 140 CLASS retval; 141 if (definition.isRoot() 142 && configuration.isFeatureEnabled(DeserializationFeature.DESERIALIZE_JSON_ROOT_PROPERTY)) { 143 144 // now parse the root property 145 retval = ObjectUtils.requireNonNull(parser.readObjectRoot( 146 definition, 147 ObjectUtils.notNull(definition.getRootJsonName()))); 148 } else { 149 // read the top-level definition 150 retval = ObjectUtils.asType(ObjectUtils.requireNonNull( 151 parser.readObject(definition))); 152 } 153 return retval; 154 } 155 } 156}