diff --git a/src/main/java/com/mindee/parsing/v2/field/ListField.java b/src/main/java/com/mindee/parsing/v2/field/ListField.java index de69bde25..56e52dd57 100644 --- a/src/main/java/com/mindee/parsing/v2/field/ListField.java +++ b/src/main/java/com/mindee/parsing/v2/field/ListField.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; import java.util.List; import java.util.StringJoiner; import lombok.AllArgsConstructor; @@ -25,6 +26,38 @@ public final class ListField extends BaseField { @JsonProperty("items") private List items; + /** + * Retrieves the {@code items} as {@code SimpleField} objects. + * + * @return a list of {@code SimpleField} objects + * @throws IllegalStateException if any dynamic field in the list is not of type {@code SIMPLE_FIELD} + */ + public List getSimpleItems() throws IllegalStateException { + List simpleItems = new ArrayList<>(); + if (items != null) { + for (DynamicField item : items) { + simpleItems.add(item.getSimpleField()); + } + } + return simpleItems; + } + + /** + * Retrieves the {@code items} as {@code ObjectField} objects. + * + * @return a list of {@code ObjectField} objects + * @throws IllegalStateException if any dynamic field in the list is not of type {@code OBJECT_FIELD} + */ + public List getObjectItems() throws IllegalStateException { + List objectItems = new ArrayList<>(); + if (items != null) { + for (DynamicField item : items) { + objectItems.add(item.getObjectField()); + } + } + return objectItems; + } + @Override public String toString() { if (items == null || items.isEmpty()) { diff --git a/src/main/java/com/mindee/parsing/v2/field/ObjectField.java b/src/main/java/com/mindee/parsing/v2/field/ObjectField.java index 5fd5561d4..b059e452f 100644 --- a/src/main/java/com/mindee/parsing/v2/field/ObjectField.java +++ b/src/main/java/com/mindee/parsing/v2/field/ObjectField.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.LinkedHashMap; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -23,6 +24,24 @@ public class ObjectField extends BaseField { @JsonProperty("fields") private InferenceFields fields; + /** + * Retrieves all sub-fields from the {@code fields} map as a {@link LinkedHashMap} of + * {@code SimpleField} objects, keyed by their field names. + * + * @return a {@link LinkedHashMap} containing the field names as keys and their corresponding + * {@code SimpleField} instances as values + * @throws IllegalStateException if any field is not of type {@code SIMPLE_FIELD} + */ + public LinkedHashMap getSimpleFields() throws IllegalStateException { + LinkedHashMap simpleFields = new LinkedHashMap<>(); + if (fields != null) { + for (String fieldName : fields.keySet()) { + simpleFields.put(fieldName, fields.getSimpleField(fieldName)); + } + } + return simpleFields; + } + @Override public String toString() { return "\n" + (fields != null ? fields.toString(1) : ""); diff --git a/src/main/java/com/mindee/parsing/v2/field/SimpleField.java b/src/main/java/com/mindee/parsing/v2/field/SimpleField.java index 73ceb9903..fec866a61 100644 --- a/src/main/java/com/mindee/parsing/v2/field/SimpleField.java +++ b/src/main/java/com/mindee/parsing/v2/field/SimpleField.java @@ -27,6 +27,35 @@ public SimpleField(Object value, FieldConfidence confidence, List this.value = value; } + /** + * Retrieves the value of the field as a string. + * + * @return the field value as a string + * @throws ClassCastException if the value cannot be cast to a string + */ + public String getStringValue() throws ClassCastException { + return (String) value; + } + + /** + * Retrieves the value of the field as a Double. + * + * @return the field value as a Double + * @throws ClassCastException if the value cannot be cast to a Double + */ + public Double getDoubleValue() throws ClassCastException { + return (Double) value; + } + + /** + * Retrieves the value of the field as a Boolean. + * + * @return the field value as a Boolean + * @throws ClassCastException if the value cannot be cast to a Boolean + */ + public Boolean getBooleanValue() throws ClassCastException { + return (Boolean) value; + } @Override public String toString() { diff --git a/src/test/java/com/mindee/parsing/v2/InferenceTest.java b/src/test/java/com/mindee/parsing/v2/InferenceTest.java index 45a0e3866..03068199f 100644 --- a/src/test/java/com/mindee/parsing/v2/InferenceTest.java +++ b/src/test/java/com/mindee/parsing/v2/InferenceTest.java @@ -12,6 +12,8 @@ import com.mindee.parsing.v2.field.ObjectField; import com.mindee.parsing.v2.field.DynamicField.FieldType; import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -188,9 +190,17 @@ void deepNestedFields_mustExposeCorrectTypes() throws IOException { @DisplayName("standard_field_types.json") class StandardFieldTypes { + private void testSimpleFieldString(SimpleField field) { + assertNotNull(field); + assertEquals(field.getValue(), field.getStringValue()); + assertThrows(ClassCastException.class, field::getDoubleValue); + assertThrows(ClassCastException.class, field::getBooleanValue); + assertInstanceOf(List.class, field.getLocations()); + } + @Test - @DisplayName("simple / object / list variants must be recognised") - void standardFieldTypes_mustExposeCorrectTypes() throws IOException { + @DisplayName("simple fields must be recognised") + void standardFieldTypes_mustExposeSimpleFieldValues() throws IOException { InferenceResponse response = loadFromResource("v2/inference/standard_field_types.json"); Inference inference = response.getInference(); assertNotNull(inference); @@ -200,20 +210,22 @@ void standardFieldTypes_mustExposeCorrectTypes() throws IOException { assertNotNull(fields.get("field_simple_string").getSimpleField()); SimpleField fieldSimpleString = fields.get("field_simple_string").getSimpleField(); - assertNotNull(fieldSimpleString); - assertInstanceOf(String.class, fieldSimpleString.getValue()); + testSimpleFieldString(fieldSimpleString); assertEquals(FieldConfidence.Certain, fieldSimpleString.getConfidence()); - assertInstanceOf(List.class, fieldSimpleString.getLocations()); assertEquals(1, fieldSimpleString.getLocations().size()); SimpleField fieldSimpleFloat = fields.get("field_simple_float").getSimpleField(); assertNotNull(fieldSimpleFloat); assertInstanceOf(Double.class, fieldSimpleFloat.getValue()); + assertEquals(fieldSimpleFloat.getValue(), fieldSimpleFloat.getDoubleValue()); + assertThrows(ClassCastException.class, fieldSimpleFloat::getStringValue); + assertThrows(ClassCastException.class, fieldSimpleFloat::getBooleanValue); assertEquals(FieldConfidence.High, fieldSimpleFloat.getConfidence()); SimpleField fieldSimpleInt = fields.get("field_simple_int").getSimpleField(); assertNotNull(fieldSimpleInt); assertInstanceOf(Double.class, fieldSimpleInt.getValue()); + assertEquals(fieldSimpleInt.getValue(), fieldSimpleInt.getDoubleValue()); assertEquals(FieldConfidence.Medium, fieldSimpleInt.getConfidence()); SimpleField fieldSimpleZero = fields.get("field_simple_zero").getSimpleField(); @@ -224,47 +236,142 @@ void standardFieldTypes_mustExposeCorrectTypes() throws IOException { SimpleField fieldSimpleBool = fields.get("field_simple_bool").getSimpleField(); assertNotNull(fieldSimpleBool); assertInstanceOf(Boolean.class, fieldSimpleBool.getValue()); + assertEquals(fieldSimpleBool.getValue(), fieldSimpleBool.getBooleanValue()); + assertThrows(ClassCastException.class, fieldSimpleBool::getStringValue); + assertThrows(ClassCastException.class, fieldSimpleBool::getDoubleValue); SimpleField fieldSimpleNull = fields.get("field_simple_null").getSimpleField(); assertNotNull(fieldSimpleNull); assertNull(fieldSimpleNull.getValue()); + assertNull(fieldSimpleNull.getStringValue()); + assertNull(fieldSimpleNull.getBooleanValue()); + assertNull(fieldSimpleNull.getDoubleValue()); + } + + @Test + @DisplayName("simple list fields must be recognised") + void standardFieldTypes_mustExposeSimpleListFieldValues() throws IOException { + InferenceResponse response = loadFromResource("v2/inference/standard_field_types.json"); + Inference inference = response.getInference(); + assertNotNull(inference); + + InferenceFields fields = inference.getResult().getFields(); + + ListField listField = fields.get("field_simple_list").getListField(); + assertNotNull(listField); + + // Low level (dynamic) access + List dynamicItems = listField.getItems(); + assertEquals(2, dynamicItems.size()); + SimpleField firstDynamicItem = dynamicItems.get(0).getSimpleField(); + assertNotNull(firstDynamicItem); + assertEquals(FieldConfidence.Medium, firstDynamicItem.getConfidence()); + assertInstanceOf(String.class, firstDynamicItem.getValue()); + for (DynamicField item : dynamicItems) { + SimpleField itemField = item.getSimpleField(); + testSimpleFieldString(itemField); + assertEquals(1, itemField.getLocations().size()); + } - ListField fieldSimpleList = fields.get("field_simple_list").getListField(); - assertNotNull(fieldSimpleList); - List simpleItems = fieldSimpleList.getItems(); + // High level (typed) access + List simpleItems = listField.getSimpleItems(); assertEquals(2, simpleItems.size()); - SimpleField firstSimpleItem = simpleItems.get(0).getSimpleField(); - assertNotNull(firstSimpleItem); + SimpleField firstSimpleItem = simpleItems.get(0); assertEquals(FieldConfidence.Medium, firstSimpleItem.getConfidence()); - assertInstanceOf(String.class, firstSimpleItem.getValue()); - for (DynamicField item : fieldSimpleList.getItems()) { - SimpleField itemField = item.getSimpleField(); - assertInstanceOf(String.class, itemField.getValue()); + for (SimpleField itemField : simpleItems) { + testSimpleFieldString(itemField); assertEquals(1, itemField.getLocations().size()); } - ObjectField fieldObject = fields.get("field_object").getObjectField(); - assertNotNull(fieldObject); - InferenceFields fieldObjectFields = fieldObject.getFields(); - assertEquals(2, fieldObjectFields.size()); - SimpleField subfield1 = fieldObjectFields.get("subfield_1").getSimpleField(); - assertInstanceOf(String.class, subfield1.getValue()); - assertEquals(FieldConfidence.High, subfield1.getConfidence()); - - ListField fieldObjectList = fields.get("field_object_list").getListField(); - assertNotNull(fieldObjectList); - List objectItems = fieldObjectList.getItems(); + assertThrows(IllegalStateException.class, listField::getObjectItems); + } + + private void testObjectSubFieldSimpleString(String fieldName, SimpleField subField) { + assertTrue(fieldName.startsWith("subfield_")); + testSimpleFieldString(subField); + } + + @Test + @DisplayName("object list fields must be recognised") + void standardFieldTypes_mustExposeObjectListFieldValues() throws IOException { + InferenceResponse response = loadFromResource("v2/inference/standard_field_types.json"); + Inference inference = response.getInference(); + assertNotNull(inference); + + InferenceFields fields = inference.getResult().getFields(); + + ListField listField = fields.get("field_object_list").getListField(); + assertNotNull(listField); + + List dynamicItems = listField.getItems(); + assertEquals(2, dynamicItems.size()); + ObjectField firstDynamicItem = dynamicItems.get(0).getObjectField(); + assertEquals( + FieldConfidence.Low, + firstDynamicItem.getFields().get("subfield_1").getSimpleField().getConfidence() + ); + for (DynamicField item : dynamicItems) { + ObjectField itemField = item.getObjectField(); + assertNotNull(itemField); + InferenceFields itemFields = itemField.getFields(); + assertEquals(2, itemFields.size()); + } + + List objectItems = listField.getObjectItems(); assertEquals(2, objectItems.size()); - ObjectField firstObjectItem = objectItems.get(0).getObjectField(); - assertNotNull(firstObjectItem); - assertInstanceOf( - String.class, - firstObjectItem.getFields().get("subfield_1").getSimpleField().getValue() + ObjectField firstObjectItem = objectItems.get(0); + assertEquals( + FieldConfidence.Low, + firstObjectItem.getSimpleFields().get("subfield_1").getConfidence() ); - for (DynamicField item : fieldObjectList.getItems()) { - SimpleField listSubfield1 = item.getObjectField().getFields().get("subfield_1").getSimpleField(); - assertInstanceOf(String.class, listSubfield1.getValue()); - assertEquals(1, listSubfield1.getLocations().size()); + for (ObjectField itemField : objectItems) { + assertNotNull(itemField); + HashMap itemFields = itemField.getSimpleFields(); + assertEquals(2, itemFields.size()); + InferenceFields itemSubFields = itemField.getFields(); + SimpleField itemSubfield1 = itemSubFields.getSimpleField("subfield_1"); + assertInstanceOf(String.class, itemSubfield1.getValue()); + for (Map.Entry entry : itemFields.entrySet()) { + String fieldName = entry.getKey(); + SimpleField subfield = entry.getValue(); + testObjectSubFieldSimpleString(fieldName, subfield); + } + } + + assertThrows(IllegalStateException.class, listField::getSimpleItems); + } + + @Test + @DisplayName("simple / object / list variants must be recognised") + void standardFieldTypes_mustExposeObjectFieldValues() throws IOException { + InferenceResponse response = loadFromResource("v2/inference/standard_field_types.json"); + Inference inference = response.getInference(); + assertNotNull(inference); + + InferenceFields fields = inference.getResult().getFields(); + + ObjectField fieldObject = fields.get("field_object").getObjectField(); + assertNotNull(fieldObject); + + InferenceFields subFieldsDynamic = fieldObject.getFields(); + assertEquals(2, subFieldsDynamic.size()); + SimpleField dynamicSubfield1 = subFieldsDynamic.get("subfield_1").getSimpleField(); + assertInstanceOf(String.class, dynamicSubfield1.getValue()); + assertEquals(FieldConfidence.High, dynamicSubfield1.getConfidence()); + for (Map.Entry entry : subFieldsDynamic.entrySet()) { + String fieldName = entry.getKey(); + SimpleField subField = entry.getValue().getSimpleField(); + testObjectSubFieldSimpleString(fieldName, subField); + } + + LinkedHashMap subFieldsSimple = fieldObject.getSimpleFields(); + assertEquals(2, subFieldsSimple.size()); + SimpleField simpleSubfield1 = subFieldsSimple.get("subfield_1"); + assertEquals(FieldConfidence.High, simpleSubfield1.getConfidence()); + for (Map.Entry entry : subFieldsSimple.entrySet()) { + String fieldName = entry.getKey(); + SimpleField subField = entry.getValue(); + testObjectSubFieldSimpleString(fieldName, subField); } } }