Skip to content

Commit 9ed7e32

Browse files
BeanTableDataSource allows for the creation of Tables on Collection of Java Beans.
1 parent a7b77af commit 9ed7e32

36 files changed

+780
-394
lines changed

docs/table-reading.md

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
# Reading tabular data
22

3+
- [Reading data the Java way](#reading-tabular-data-the-java-way) explains various ways of reading data
4+
as instances of a provided Bean class
5+
- [Reading from CSV/JSON data](#reading-from-CSV/JSON-data) Read data and transparently convert them
6+
to Java Beans.
7+
- [Creating a Table from Java Bean Objects](#creating-a-table-from-java-bean-objects) Construct a Table
8+
on a Collection of Java Beans.
9+
- [Reading data the Frictionless way](#reading-tabular-data-the-frictionless-way) - either as String Arrays,
10+
Object Arrays, or Maps
11+
- [Without a Schema](#reading-tabular-data-without-a-schema) Read as String arrays and without
12+
format integrity assurance
13+
- [With a Schema](#reading-tabular-data-using-a-schema) Read as converted Java object arrays with
14+
format integrity assurance
15+
- [Read data as Maps](#reading-tabular-data-returning-rows-as-maps) if you prefer your data as
16+
key/value pairs
17+
318
There are basically two distinct ways to read data with the help of the tableschema-java library:
419
- The way other language implementation of the Frictionless Tableschema standard handle this
520
- In a true native Java way
@@ -17,6 +32,11 @@ Since Java is a strongly typed language, we can derive the data type for each co
1732
Java bean. This allows us to model our domain model in terms of Java beans, enjoy the format integrity
1833
of a Table Schema, and while reading data, get it returned as instances of our bean class.
1934

35+
Some limitations apply: the Bean serialization/deserialization does not know all the bells and whistles
36+
that e.g. Jackson does and you can't use nested data - after all, tabular data is not hierarchical and
37+
is not well suited for object trees. Also, you can't use custom classes for your members, you need
38+
to stick to a pre-defined list of classes.
39+
2040
First we define a Bean class:
2141
```java
2242
public class SimpleDataBean {
@@ -47,7 +67,7 @@ public class SimpleDataBean {
4767
}
4868
}
4969
```
50-
70+
### Reading from CSV/JSON data
5171
And with that, we can read the data as `SimpleDataBean` instances:
5272
```java
5373
URL url = new URL("https://raw.githubusercontent.com/frictionlessdata/tableschema-java/master" +
@@ -67,6 +87,28 @@ while (bit.hasNext()) {
6787
// 3 baz
6888
```
6989

90+
### Creating a Table from Java Bean Objects
91+
92+
If we have a Collection of our business objects as data, we can construct a Table with the data as backing.
93+
All the iterators from the other examples are available for reading the serialized data - or a Data Package
94+
could be written.
95+
96+
You can export a synthesized Schema based on the Bean class. It is a regular Table Schema.
97+
98+
```java
99+
List<EmployeeBeanWithAnnotation> employees = new ArrayList<>();
100+
101+
Table t = new Table(employees, EmployeeBeanWithAnnotation.class);
102+
103+
Schema schema = t.getSchema();
104+
Iterator<Object[]> iter = table.iterator();
105+
while(iter.hasNext()){
106+
Object[] row = iter.next();
107+
System.out.println(Arrays.toString(row));
108+
}
109+
```
110+
111+
70112
## Reading tabular data the Frictionless way
71113

72114
### Reading tabular data without a Schema

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<modelVersion>4.0.0</modelVersion>
44
<groupId>io.frictionlessdata</groupId>
55
<artifactId>tableschema-java</artifactId>
6-
<version>0.6.0-SNAPSHOT</version>
6+
<version>0.6.1-SNAPSHOT</version>
77
<packaging>jar</packaging>
88
<issueManagement>
99
<url>https://github.com/frictionlessdata/tableschema-java/issues</url>

src/main/java/io/frictionlessdata/tableschema/Table.java

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.frictionlessdata.tableschema;
22

3+
import io.frictionlessdata.tableschema.schema.BeanSchema;
4+
import io.frictionlessdata.tableschema.tabledatasource.BeanTableDataSource;
35
import io.frictionlessdata.tableschema.tabledatasource.CsvTableDataSource;
46
import io.frictionlessdata.tableschema.tabledatasource.StringArrayTableDataSource;
57
import io.frictionlessdata.tableschema.tabledatasource.TableDataSource;
@@ -55,6 +57,18 @@ public class Table{
5557
*/
5658
public Table() { }
5759

60+
/**
61+
* Constructor for a Table from a Collection of Java Beans and the Bean class.
62+
* @param data a {@link java.util.Collection} holding Java Beans of type `type`
63+
* @param type the Bean class
64+
*/
65+
public Table(Collection<?> data, Class<?> type) {
66+
BeanTableDataSource ds = new BeanTableDataSource(data, type);
67+
this.dataSource = ds;
68+
schema = BeanSchema.infer(ds.getBeanClass());
69+
validate();
70+
}
71+
5872
/**
5973
* Constructor for a Table from String array input data and optionally
6074
* a Schema. Data is parsed into a TableDataSource object
@@ -69,17 +83,24 @@ public Table(Collection<String[]> data, String[] headers, Schema schema) {
6983
validate();
7084
}
7185

86+
public static Table fromSource(BeanTableDataSource dataSource) {
87+
Table table = new Table();
88+
table.dataSource = dataSource;
89+
table.schema = BeanSchema.infer(dataSource.getBeanClass());
90+
return table;
91+
}
92+
7293
/**
7394
* Create Table on an {@link java.io.InputStream} for reading both the CSV/JSON
7495
* data and the table schema.
75-
* @param dataSource InputStream for reading the data from
96+
* @param data InputStream for reading the data from
7697
* @param schema InputStream for reading table schema from. Can be `null`
77-
* @param format The expected CSVFormat if dataSource is a CSV-containing InputStream; ignored for JSON data.
98+
* @param format The expected CSVFormat if data is a CSV-containing InputStream; ignored for JSON data.
7899
* Can be `null`
79100
*/
80-
public static Table fromSource(InputStream dataSource, InputStream schema, CSVFormat format){
101+
public static Table fromSource(InputStream data, InputStream schema, CSVFormat format){
81102
Table table = new Table();
82-
table.dataSource = TableDataSource.fromSource(dataSource);
103+
table.dataSource = TableDataSource.fromSource(data);
83104
if (null != schema) {
84105
try {
85106
table.schema = Schema.fromJson(schema, true);

src/main/java/io/frictionlessdata/tableschema/annotations/FieldFormat.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@
3838
*
3939
* From the specs. https://specs.frictionlessdata.io/table-schema/#types-and-formats
4040
* date/time values in this field can be parsed according to
41-
* <PATTERN>. <PATTERN> MUST follow the syntax of standard Python / C
41+
* &lt;PATTERN&gt;. &lt;PATTERN&gt; MUST follow the syntax of standard Python / C
4242
* strptime (That is, values in the this field should be parsable
43-
* by Python / C standard strptime using <PATTERN>).
43+
* by Python / C standard strptime using &lt;PATTERN&gt;).
4444
* Example for "format": "%d/%m/%y" which would correspond to dates like: 30/11/14
4545
*/
4646
String format() default "default";

src/main/java/io/frictionlessdata/tableschema/exception/ValidationException.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
import io.frictionlessdata.tableschema.schema.FormalSchemaValidator;
55

66
import java.util.*;
7+
import java.util.function.Function;
8+
import java.util.stream.Collectors;
79

810
public class ValidationException extends TableSchemaException {
911

1012
List<ValidationMessage> validationMessages = new ArrayList<>();
13+
List<String> otherMessages = new ArrayList<>();
1114

1215
public ValidationException(String msg) {
1316
super(msg);
@@ -22,17 +25,21 @@ public ValidationException(FormalSchemaValidator schema, Collection<ValidationMe
2225
this.validationMessages.addAll(messages);
2326
}
2427

25-
public ValidationException(JsonSchema schema, Collection<ValidationMessage> messages) {
26-
this(String.format("%s: %s", schema, "validation failed"));
27-
this.validationMessages.addAll(messages);
28-
}
29-
3028
public ValidationException(String schemaName, Collection<ValidationException> exceptions) {
31-
this(String.format("%s: %s", schemaName, "validation failed"));
29+
this(String.format("%s: %s", schemaName, "validation failed: "));
30+
otherMessages.addAll(exceptions
31+
.stream().map((Throwable::getMessage)).collect(Collectors.toList()));
3232
final Set<ValidationMessage> messages = new LinkedHashSet<>();
3333
exceptions.forEach((m) -> {
3434
messages.addAll(m.validationMessages);
3535
});
3636
this.validationMessages.addAll(messages);
3737
}
38+
39+
public List<Object> getMessages() {
40+
List<Object> retVal = new ArrayList<>();
41+
retVal.addAll(validationMessages);
42+
retVal.addAll(otherMessages);
43+
return retVal;
44+
}
3845
}

src/main/java/io/frictionlessdata/tableschema/field/AnyField.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ public String formatValueAsString(Object value, String format, Map<String, Objec
3939
return value.toString();
4040
}
4141

42+
@Override
43+
String formatObjectValueAsString(Object value, String format, Map<String, Object> options) throws InvalidCastException, ConstraintsException {
44+
return value.toString();
45+
}
46+
4247

4348
@Override
4449
public String parseFormat(String value, Map<String, Object> options) {

src/main/java/io/frictionlessdata/tableschema/field/ArrayField.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@
88
import io.frictionlessdata.tableschema.util.JsonUtil;
99

1010
import java.net.URI;
11-
import java.util.ArrayList;
12-
import java.util.Arrays;
13-
import java.util.List;
14-
import java.util.Map;
11+
import java.util.*;
1512
import java.util.stream.Collectors;
1613

1714

@@ -43,6 +40,32 @@ public Object[] parseValue(String value, String format, Map<String, Object> opti
4340
@Override
4441
public String formatValueAsString(Object[] value, String format, Map<String, Object> options)
4542
throws InvalidCastException, ConstraintsException {
43+
return _format(value);
44+
/*List<String> vals = new ArrayList<>();
45+
String val;
46+
for (Object o : value) {
47+
if (o instanceof String) {
48+
val = "\""+o+"\"";
49+
} else {
50+
Field f = FieldInferrer.infer(o);
51+
val = f.formatValueAsString(o);
52+
}
53+
vals.add(val);
54+
}
55+
return "[" + vals.stream().collect(Collectors.joining(",")) +"]";*/
56+
}
57+
58+
@Override
59+
String formatObjectValueAsString(Object value, String format, Map<String, Object> options) throws InvalidCastException, ConstraintsException {
60+
if (value instanceof Collection) {
61+
Collection vals = (Collection)value;
62+
return _format(vals.toArray(new Object[0]));
63+
//return "[" + vals.stream().collect(Collectors.joining(",")) +"]";
64+
}
65+
return value.toString();
66+
}
67+
68+
private String _format(Object... value) {
4669
List<String> vals = new ArrayList<>();
4770
String val;
4871
for (Object o : value) {
@@ -57,7 +80,6 @@ public String formatValueAsString(Object[] value, String format, Map<String, Obj
5780
return "[" + vals.stream().collect(Collectors.joining(",")) +"]";
5881
}
5982

60-
6183
@Override
6284
public String parseFormat(String value, Map<String, Object> options) {
6385
return "default";

src/main/java/io/frictionlessdata/tableschema/field/BooleanField.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public String formatValueAsString(Boolean value) throws InvalidCastException, Co
7070
return (value) ? _getActualTrueValues().get(0) : _getActualFalseValues().get(0);
7171
}
7272

73+
7374
@Override
7475
public String formatValueAsString(Boolean value, String format, Map<String, Object> options) throws InvalidCastException, ConstraintsException {
7576
String trueValue = _getActualTrueValues().get(0);
@@ -85,6 +86,12 @@ public String formatValueAsString(Boolean value, String format, Map<String, Obje
8586
return (value) ? trueValue : falseValue;
8687
}
8788

89+
90+
@Override
91+
String formatObjectValueAsString(Object value, String format, Map<String, Object> options) throws InvalidCastException, ConstraintsException {
92+
return value.toString();
93+
}
94+
8895
@Override
8996
public String parseFormat(String value, Map<String, Object> options) {
9097
return "default";

src/main/java/io/frictionlessdata/tableschema/field/DateField.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ public String formatValueAsString(LocalDate value, String format, Map<String, Ob
8383
return value.format(DateTimeFormatter.ofPattern(translatedFormat));
8484
}
8585

86+
@Override
87+
String formatObjectValueAsString(Object value, String format, Map<String, Object> options) throws InvalidCastException, ConstraintsException {
88+
return value.toString();
89+
}
8690

8791
@Override
8892
public String parseFormat(String value, Map<String, Object> options) {

src/main/java/io/frictionlessdata/tableschema/field/DatetimeField.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ public String formatValueAsString(ZonedDateTime value, String format, Map<String
5454
return value.format(formatter);
5555
}
5656

57+
@Override
58+
String formatObjectValueAsString(Object value, String format, Map<String, Object> options) throws InvalidCastException, ConstraintsException {
59+
return value.toString();
60+
}
5761

5862
@Override
5963
public String parseFormat(String value, Map<String, Object> options) {

0 commit comments

Comments
 (0)