Skip to content

Commit

Permalink
feat(json): configure default type to associate to Json instances
Browse files Browse the repository at this point in the history
ING-3128
  • Loading branch information
stempler committed Aug 21, 2023
1 parent c076a1b commit 000aab6
Show file tree
Hide file tree
Showing 6 changed files with 290 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class JsonInstanceCollectionTest {

@Test
void testReadFeatureCollection() {
def translate = new JsonToInstance(true, simpleType, null, SimpleLog.CONSOLE_LOG)
def translate = new JsonToInstance(true, simpleType, false, null, SimpleLog.CONSOLE_LOG)
def collection = new JsonInstanceCollection(translate, new StringInputSupplier(testDataSimpleFc, charset), charset)
collection.iterator().withCloseable {
def count = 0
Expand All @@ -114,7 +114,7 @@ class JsonInstanceCollectionTest {

@Test
void testSkipFeatureCollection() {
def translate = new JsonToInstance(true, simpleType, null, SimpleLog.CONSOLE_LOG)
def translate = new JsonToInstance(true, simpleType, false, null, SimpleLog.CONSOLE_LOG)
def collection = new JsonInstanceCollection(translate, new StringInputSupplier(testDataSimpleFc, charset), charset)
collection.iterator().withCloseable {
def count = 0
Expand All @@ -130,7 +130,7 @@ class JsonInstanceCollectionTest {

@Test
void testReadGeoJsonArray() {
def translate = new JsonToInstance(true, simpleType, null, SimpleLog.CONSOLE_LOG)
def translate = new JsonToInstance(true, simpleType, false, null, SimpleLog.CONSOLE_LOG)

def testData = '''
[
Expand Down Expand Up @@ -181,7 +181,7 @@ class JsonInstanceCollectionTest {

@Test
void testReadJsonArray() {
def translate = new JsonToInstance(false, simpleType, null, SimpleLog.CONSOLE_LOG)
def translate = new JsonToInstance(false, simpleType, false, null, SimpleLog.CONSOLE_LOG)

def testData = '''
[
Expand Down Expand Up @@ -226,7 +226,7 @@ class JsonInstanceCollectionTest {

@Test
void testReadSingleObject() {
def translate = new JsonToInstance(JsonReadMode.singleObject, false, simpleType, null, SimpleLog.CONSOLE_LOG)
def translate = new JsonToInstance(JsonReadMode.singleObject, false, simpleType, false, null, SimpleLog.CONSOLE_LOG)

def testData = '''
{
Expand Down Expand Up @@ -258,7 +258,7 @@ class JsonInstanceCollectionTest {

@Test
void testReadFirstArray() {
def translate = new JsonToInstance(JsonReadMode.firstArray, false, simpleType, null, SimpleLog.CONSOLE_LOG)
def translate = new JsonToInstance(JsonReadMode.firstArray, false, simpleType, false, null, SimpleLog.CONSOLE_LOG)

def testData = '''
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import static org.assertj.core.api.Assertions.*
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets

import javax.xml.namespace.QName

import org.junit.Test

import eu.esdihumboldt.hale.common.core.io.impl.LogProgressIndicator
Expand All @@ -43,10 +45,16 @@ class JsonInstanceReaderTest {
// simple type and schema used in some tests
TypeDefinition simpleType

TypeDefinition altType

Schema simpleSchema = new SchemaBuilder().schema {
simpleType = SimpleType {
text(String)
}
altType = AltType {
id(String)
text(String)
}
}

// default charset used in tests
Expand Down Expand Up @@ -110,6 +118,105 @@ class JsonInstanceReaderTest {
}
}

@Test
void testReadDefaultType() {
def reader = simpleJsonReader(testDataSimple)

def typeName = QName.valueOf('SimpleType')
reader.setDefaultType(typeName)

def collection = execute(reader)
collection.iterator().withCloseable {
def count = 0
while (it.hasNext()) {
def instance = it.next()

assertThat(instance.definition.name).isEqualTo(typeName)
checkSimpleInstance(instance, count)

count++
}

// test expected feature count
assertThat(count).isEqualTo(2)
}
}

@Test
void testReadDefaultTypeOverride() {
def testData = '''
[
{
"text": "foo",
"@type": "AltType"
},
{
"@type": "AltType",
"text": "bar"
}
]
'''
def reader = simpleJsonReader(testData)

def typeName = QName.valueOf('SimpleType')
reader.setDefaultType(typeName)

def expectedTypeName = QName.valueOf('AltType')

def collection = execute(reader)
collection.iterator().withCloseable {
def count = 0
while (it.hasNext()) {
def instance = it.next()

assertThat(instance.definition.name).isEqualTo(expectedTypeName)
checkSimpleInstance(instance, count)

count++
}

// test expected feature count
assertThat(count).isEqualTo(2)
}
}

@Test
void testReadForceDefaultType() {
def testData = '''
[
{
"text": "foo",
"@type": "AltType"
},
{
"@type": "AltType",
"text": "bar"
}
]
'''
def reader = simpleJsonReader(testData)

def typeName = QName.valueOf('SimpleType')
reader.setDefaultType(typeName)
reader.setForceDefaultType(true)

def collection = execute(reader)
collection.iterator().withCloseable {
def count = 0
while (it.hasNext()) {
def instance = it.next()

assertThat(instance.definition.name).isEqualTo(typeName)
checkSimpleInstance(instance, count)

count++
}

// test expected feature count
assertThat(count).isEqualTo(2)
}
}

// helpers

InstanceCollection execute(JsonInstanceReader reader) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ class JsonToInstanceTest {
* @return the parsed instance
*/
Instance readInstance(String json, TypeDefinition type, Schema schema, boolean expectGeoJson = false) {
def translate = new JsonToInstance(expectGeoJson, type, schema, SimpleLog.CONSOLE_LOG)
def translate = new JsonToInstance(expectGeoJson, type, false, schema, SimpleLog.CONSOLE_LOG)
JsonParser parser = new ObjectMapper().getJsonFactory().createJsonParser(json)
assertThat(parser.nextToken()).isEqualTo(JsonToken.START_OBJECT)
translate.readInstance(parser)
Expand Down
25 changes: 25 additions & 0 deletions io/plugins/eu.esdihumboldt.hale.io.json/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,31 @@
sampleDescription="When the source file is only a single object at its root, use this mode">
</valueDescriptor>
</providerParameter>
<providerParameter
description="Always use default type for all objects read"
label="Force default type"
name="forceDefaultType"
optional="true">
<parameterBinding
class="java.lang.Boolean">
</parameterBinding>
<valueDescriptor
default="false"
defaultDescription="By default allow other mechanism to determine an instance type"
sample="true"
sampleDescription="Use the configured default type for all instances read">
</valueDescriptor>
</providerParameter>
<providerParameter
description="Name of the default type to use for read Json objects"
label="Default type"
name="defaultType"
optional="true"
valueDescriptor="eu.esdihumboldt.hale.io.json.JsonInstanceReader.DefaultTypeParameterDescriptor">
<parameterBinding
class="java.lang.String">
</parameterBinding>
</providerParameter>
</provider>
</extension>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@

import java.io.IOException;

import javax.xml.namespace.QName;

import de.fhg.igd.slf4jplus.ALogger;
import de.fhg.igd.slf4jplus.ALoggerFactory;
import eu.esdihumboldt.hale.common.core.io.IOProviderConfigurationException;
import eu.esdihumboldt.hale.common.core.io.ProgressIndicator;
import eu.esdihumboldt.hale.common.core.io.Value;
import eu.esdihumboldt.hale.common.core.io.report.IOReport;
import eu.esdihumboldt.hale.common.core.io.report.IOReporter;
import eu.esdihumboldt.hale.common.core.parameter.AbstractParameterValueDescriptor;
import eu.esdihumboldt.hale.common.core.report.SimpleLog;
import eu.esdihumboldt.hale.common.instance.io.impl.AbstractInstanceReader;
import eu.esdihumboldt.hale.common.instance.model.InstanceCollection;
Expand All @@ -41,11 +44,36 @@ public class JsonInstanceReader extends AbstractInstanceReader {

private static final ALogger log = ALoggerFactory.getLogger(JsonInstanceReader.class);

@SuppressWarnings("javadoc")
public static class DefaultTypeParameterDescriptor extends AbstractParameterValueDescriptor {

public DefaultTypeParameterDescriptor() {
super(null, Value.of(new QName("namespace", "localname").toString()));
}

@Override
public String getSampleDescription() {
return "The type name is represented like in the given example, with the namespace in curly braces.";
}
}

/**
* Name of the parameter that specifies the read mode.
*/
public static final String PARAM_READ_MODE = "mode";

/**
* Name of the parameter that specifies the default type to assume for an
* instance.
*/
public static final String PARAM_DEFAULT_TYPE = "defaultType";

/**
* Name of the parameter that specifies if the default type should be used
* for all instances (i.e. use no other mechanisms to detect the type).
*/
public static final String PARAM_FORCE_DEFAULT_TYPE = "forceDefaultType";

private InstanceCollection instances;

/**
Expand All @@ -55,6 +83,8 @@ public JsonInstanceReader() {
super();

addSupportedParameter(PARAM_READ_MODE);
addSupportedParameter(PARAM_DEFAULT_TYPE);
addSupportedParameter(PARAM_FORCE_DEFAULT_TYPE);
}

@Override
Expand All @@ -77,13 +107,14 @@ protected IOReport execute(ProgressIndicator progress, IOReporter reporter)
boolean expectGeoJson = true; // currently defaults to true, no
// major difference in functionality

// FIXME support configuring type; possibly also type detection
// XXX for now first type found in schema is used
TypeDefinition type = getSourceSchema().getMappingRelevantTypes().stream().findFirst()
.orElse(null);
QName defaultTypeName = getDefaultType();
TypeDefinition type = null;
if (defaultTypeName != null) {
type = getSourceSchema().getType(defaultTypeName);
}

JsonToInstance translator = new JsonToInstance(getReadMode(), expectGeoJson, type,
getSourceSchema(), SimpleLog.fromLogger(log));
isForceDefaultType(), getSourceSchema(), SimpleLog.fromLogger(log));
instances = new JsonInstanceCollection(translator, getSource(), getCharset());

reporter.setSuccess(true);
Expand All @@ -102,7 +133,12 @@ protected IOReport execute(ProgressIndicator progress, IOReporter reporter)
* @param mode the mode for reading Json
*/
public void setReadMode(JsonReadMode mode) {
setParameter(PARAM_READ_MODE, Value.of(mode.toString()));
if (mode == null) {
setParameter(PARAM_READ_MODE, Value.NULL);
}
else {
setParameter(PARAM_READ_MODE, Value.of(mode.toString()));
}
}

/**
Expand All @@ -116,6 +152,52 @@ public JsonReadMode getReadMode() {
return value;
}

/**
* Set the default type to use for read instances. Other mechanisms to
* determine the type may take precedence.
*
* @param defaultType the name of the default type to use
*/
public void setDefaultType(QName defaultType) {
if (defaultType == null) {
setParameter(PARAM_DEFAULT_TYPE, Value.NULL);
}
else {
setParameter(PARAM_DEFAULT_TYPE, Value.of(defaultType.toString()));
}
}

/**
* @return the name of the default type to use for read instances, may be
* <code>null</code>
*/
public QName getDefaultType() {
String name = getParameter(PARAM_DEFAULT_TYPE).as(String.class);
if (name != null) {
return QName.valueOf(name);
}
return null;
}

/**
* Set if the default type specified should be forced to be used for all
* instances. This disables any other mechanisms to determine the type of
* the instance.
*
* @param force <code>true</code> if the configured default type should
* always be used, <code>false</code> otherwise
*/
public void setForceDefaultType(boolean force) {
setParameter(PARAM_FORCE_DEFAULT_TYPE, Value.of(force));
}

/**
* @return if the default type should be forced to be used for all instances
*/
public boolean isForceDefaultType() {
return getParameter(PARAM_FORCE_DEFAULT_TYPE).as(Boolean.class, false);
}

@Override
protected String getDefaultTypeName() {
return "JSON";
Expand Down
Loading

0 comments on commit 000aab6

Please sign in to comment.