Skip to content

Commit

Permalink
feat(json): basic support for reading complex properties
Browse files Browse the repository at this point in the history
ING-3128
  • Loading branch information
stempler committed Aug 21, 2023
1 parent 000aab6 commit 7ed3aa4
Show file tree
Hide file tree
Showing 3 changed files with 236 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -393,8 +393,14 @@ class InstanceBuilder extends BuilderBase {
value = params[0]
}

// create instance
return createInstanceAndValue(property.propertyType, value)
if (value instanceof Instance) {
// allow providing an instance as value
return value
}
else {
// create instance
return createInstanceAndValue(property.propertyType, value)
}
}
else {
// normal property value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -451,11 +451,196 @@ class JsonToInstanceTest {
} as Consumer)
}

/**
* Test reading a Json object with nested properties.
*/
@Test
void testReadComplex() {
TypeDefinition type
Schema schema = new SchemaBuilder().schema {
def nameType = NameType {
en(String)
de(String)
}

type = TestType {
id(String)
name(nameType)
}
}

def json = '''{
"id": "test",
"name": {
"en": "corn",
"de": "Mais"
}
}'''

def instance = readInstance(json, type, null, true)

def id = instance.p.id.value()
assertThat(id).isEqualTo('test')

def name = instance.p.name.first()
assertThat(name).isInstanceOf(Instance)

assertThat(name.p.en.value()).isEqualTo('corn')
assertThat(name.p.de.value()).isEqualTo('Mais')
}

/**
* Test reading a Json object with multiple layers of nested properties.
*/
@Test
void testReadComplexNested() {
TypeDefinition type
Schema schema = new SchemaBuilder().schema {
type = TestType {
product {
name {
en(String)
de(String)
}
}
}
}

def json = '''{
"product": {
"name": {
"en": "corn",
"de": "Mais"
}
}
}'''

def instance = readInstance(json, type, null, true)

def product = instance.p.product.first()
assertThat(product).isInstanceOf(Instance)

def name = product.p.name.first()
assertThat(name).isInstanceOf(Instance)

assertThat(name.p.en.value()).isEqualTo('corn')
assertThat(name.p.de.value()).isEqualTo('Mais')
}

/**
* Test reading a Json object with a simple array property.
*/
@Test
void testReadSimpleArray() {
TypeDefinition type
Schema schema = new SchemaBuilder().schema {
type = TestType {
item(cardinality: '1..n', String)
}
}

def json = '''{
"item": [
"foo",
"bar"
]
}'''

def instance = readInstance(json, type, null, true)

List values = instance.p.item.values()
assertThat(values).containsExactly('foo', 'bar')
}

/**
* Test reading a Json object with multiple layers of nested properties.
*/
@Test
void testReadArrayNested() {
TypeDefinition type
Schema schema = new SchemaBuilder().schema {
type = TestType {
product(cardinality: '0..n') {
name {
en(String)
de(String)
}
}
}
}

def json = '''{
"product": [
{
"name": {
"en": "corn",
"de": "Mais"
}
},
{
"name": {
"en": "cucumber",
"de": "Grüne Banane"
}
}
]
}'''

def instance = readInstance(json, type, null, true)

List products = instance.p.product.list()
assertThat(products).hasSize(2)

assertThat(products[0].p.name.en.value()).isEqualTo('corn')
assertThat(products[0].p.name.de.value()).isEqualTo('Mais')

assertThat(products[1].p.name.en.value()).isEqualTo('cucumber')
assertThat(products[1].p.name.de.value()).isEqualTo('Grüne Banane')
}

/**
* Test reading a Json object with multiple layers of nested properties.
*/
@Test
void testReadSimpleChoice() {
TypeDefinition type
Schema schema = new SchemaBuilder().schema {
type = TestType {
product(cardinality: '0..n') {
_(choice: true) {
name_en(String)
name_de(String)
}
}
}
}

def json = '''{
"product": [
{
"name_de": "Mais"
},
{
"name_en": "cucumber"
}
]
}'''

def instance = readInstance(json, type, null, true)

List products = instance.p.product.list()
assertThat(products).hasSize(2)

assertThat(products[0].p.name_de.value()).isEqualTo('Mais')
assertThat(products[0].p.name_en.value()).isNull()

assertThat(products[1].p.name_de.value()).isNull()
assertThat(products[1].p.name_en.value()).isEqualTo('cucumber')
}

/*
* TODO add additional tests for specific cases:
* - complex properties / arrays
* - any specific bindings that should be supported?
* Later:
*/

// helper methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
*/
package eu.esdihumboldt.hale.io.json.internal

import java.util.Map.Entry

import javax.xml.namespace.QName

import org.geotools.geojson.geom.GeometryJSON
Expand Down Expand Up @@ -65,7 +67,11 @@ class JsonInstanceBuilder {
this.geometryJson = new GeometryJSON()
this.defaultCrs = defaultCrs

builder = new InstanceBuilder(
builder = createBuilder()
}

protected InstanceBuilder createBuilder() {
new InstanceBuilder(
strictBinding: false,
strictValueFlags: false // allow setting values even if no value is expected (mainly for use with XML schema and geometries)
)
Expand All @@ -81,6 +87,19 @@ class JsonInstanceBuilder {
*/
public Instance buildInstance(TypeDefinition type, ObjectNode geom,
Map<String, JsonNode> properties) {
buildInstance(builder, type, geom, properties)
}

/**
* Build an instance of the given type.
*
* @param type the type definition or <code>null</code> if without schema
* @param geom a dedicated (GeoJson) geometry object or <code>null</code>
* @param properties map of properties or <code>null</code>
* @return the created instance
*/
protected Instance buildInstance(InstanceBuilder builder, TypeDefinition type, ObjectNode geom,
Map<String, JsonNode> properties) {
if (type == null) {
// TODO implement schema-less mode
throw new UnsupportedOperationException("Schema-less mode not implemented yet");
Expand Down Expand Up @@ -123,7 +142,7 @@ class JsonInstanceBuilder {
else {
if (value.isArray()) {
// add each value
def iterator = value.getElements()
def iterator = value.elements()
while (iterator.hasNext()) {
JsonNode item = iterator.next()
def itemValue = translateValue(item, property)
Expand Down Expand Up @@ -221,9 +240,26 @@ class JsonInstanceBuilder {
}
}
else {
//FIXME handle complex properties?
//XXX for now ignore
null
// handle complex properties

ObjectNode fields = (value != null && value.isObject()) ? (ObjectNode) value
: null;
Map<String, JsonNode> properties = new HashMap<>();
if (fields != null) {
Iterator<Entry<String, JsonNode>> it = fields.fields();
while (it.hasNext()) {
Entry<String, JsonNode> entry = it.next();
properties.put(entry.getKey(), entry.getValue());
}

// build instance with new builder instance
buildInstance(createBuilder(), property.propertyType, null, properties)
}
else {
// not an object
//TODO throw/report error if not null?
null
}
}
}

Expand Down

0 comments on commit 7ed3aa4

Please sign in to comment.