Skip to content

Commit 7572cfe

Browse files
Buf fix for data resources that should be serialized to file
1 parent 580216a commit 7572cfe

File tree

7 files changed

+231
-39
lines changed

7 files changed

+231
-39
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<modelVersion>4.0.0</modelVersion>
55
<groupId>io.frictionlessdata</groupId>
66
<artifactId>datapackage-java</artifactId>
7-
<version>0.9.1-SNAPSHOT</version>
7+
<version>0.9.2-SNAPSHOT</version>
88
<packaging>jar</packaging>
99
<issueManagement>
1010
<url>https://github.com/frictionlessdata/datapackage-java/issues</url>

src/main/java/io/frictionlessdata/datapackage/Package.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -606,17 +606,17 @@ public void write (File outputDir, Consumer<Path> callback, boolean zipCompresse
606606
for (Resource r : resourceList) {
607607
r.writeData(outFs.getPath(parentDirName ));
608608
r.writeSchema(outFs.getPath(parentDirName));
609+
r.writeDialect(outFs.getPath(parentDirName));
609610

610611
// write out dialect file only if not null or URL
611-
String dialectP = r.getPathForWritingDialect();
612-
if (null != dialectP) {
613-
Path dialectPath = outFs.getPath(parentDirName + File.separator + dialectP);
614-
r.getDialect().writeDialect(dialectPath);
615-
}
616-
Dialect dia = r.getDialect();
612+
613+
/*Dialect dia = r.getDialect();
617614
if (null != dia) {
615+
String dialectP = r.getPathForWritingDialect();
616+
Path dialectPath = outFs.getPath(parentDirName + File.separator + dialectP);
617+
dia.writeDialect(dialectPath);
618618
dia.setReference(new LocalFileReference(new File(dialectP)));
619-
}
619+
}*/
620620
}
621621
writeDescriptor(outFs, parentDirName);
622622

@@ -750,6 +750,8 @@ private ObjectNode getJsonNode(){
750750
} else {
751751
obj.set(JSON_KEY_PATH, JsonUtil.getInstance().createArrayNode(outPaths));
752752
}
753+
// If a data resource should be saved to file, it should not be inlined as well
754+
obj.remove(JSON_KEY_DATA);
753755
obj.put(JSON_KEY_FORMAT, resource.getSerializationFormat());
754756
}
755757
obj.remove("originalReferences");

src/main/java/io/frictionlessdata/datapackage/resource/AbstractDataResource.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ public abstract class AbstractDataResource<T> extends AbstractResource<T> {
3434
throw new DataPackageException("Invalid Resource. The data property cannot be null for a Data-based Resource.");
3535
}
3636

37-
/**
38-
* @return the data
39-
*/
37+
4038
@Override
4139
@JsonProperty(JSON_KEY_DATA)
4240
public Object getRawData() throws IOException {

src/main/java/io/frictionlessdata/datapackage/resource/AbstractResource.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,17 @@ public Object getSchemaForJson() {
9191
return null;
9292
}
9393

94+
@JsonProperty(JSON_KEY_DIALECT)
95+
public Object getDialectForJson() {
96+
if (originalReferences.containsKey(JSON_KEY_DIALECT)) {
97+
return originalReferences.get(JSON_KEY_DIALECT);
98+
}
99+
if (null != dialect) {
100+
return dialect;
101+
}
102+
return null;
103+
}
104+
94105
@Override
95106
public Iterator<Object[]> objectArrayIterator() throws Exception{
96107
return this.objectArrayIterator(false, false);
@@ -484,6 +495,33 @@ private static void writeSchema(Path parentFilePath, Schema schema) throws IOExc
484495
}
485496
}
486497

498+
public void writeDialect(Path parentFilePath) throws IOException {
499+
if (null == dialect)
500+
return;
501+
String relPath = getPathForWritingDialect();
502+
if (null == originalReferences.get(JSONBase.JSON_KEY_DIALECT) && Objects.nonNull(relPath)) {
503+
originalReferences.put(JSONBase.JSON_KEY_DIALECT, relPath);
504+
}
505+
506+
if (null != relPath) {
507+
boolean isRemote;
508+
try {
509+
// don't try to write schema files that came from remote, let's just add the URL to the descriptor
510+
URI uri = new URI(relPath);
511+
isRemote = (null != uri.getScheme()) &&
512+
(uri.getScheme().equals("http") || uri.getScheme().equals("https"));
513+
if (isRemote)
514+
return;
515+
} catch (URISyntaxException ignored) {}
516+
Path p;
517+
if (parentFilePath.toString().isEmpty()) {
518+
p = parentFilePath.getFileSystem().getPath(relPath);
519+
} else {
520+
p = parentFilePath.resolve(relPath);
521+
}
522+
writeDialect(p, dialect);
523+
}
524+
}
487525

488526
private static void writeDialect(Path parentFilePath, Dialect dialect) throws IOException {
489527
if (!Files.exists(parentFilePath)) {
@@ -583,6 +621,7 @@ public String getProfile() {
583621
* @return the dialect
584622
*/
585623
@Override
624+
@JsonIgnore
586625
public Dialect getDialect() {
587626
return dialect;
588627
}

src/main/java/io/frictionlessdata/datapackage/resource/Resource.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,15 @@ public interface Resource<T> extends BaseInterface {
209209
*/
210210
void writeSchema(Path parentFilePath) throws IOException;
211211

212+
/**
213+
* Write the Resource {@link Dialect} to `outputDir`.
214+
*
215+
* @param parentFilePath the directory to write to. Code must create
216+
* files as needed.
217+
* @throws IOException if something fails while writing
218+
*/
219+
void writeDialect(Path parentFilePath) throws IOException;
220+
212221
/**
213222
* Returns an Iterator that returns rows as object-arrays. Values in each column
214223
* are parsed and converted ("cast") to Java objects based on the Field definitions of the Schema.

src/test/java/io/frictionlessdata/datapackage/resource/NonTabularResourceTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ public void writeData(Writer writer) {
254254
public void writeSchema(Path path) {
255255
}
256256

257+
@Override
258+
public void writeDialect(Path parentFilePath) throws IOException {
259+
}
260+
257261
@Override
258262
public Iterator<Object[]> objectArrayIterator() {
259263
throw new UnsupportedOperationException("Not supported on non-tabular Resources");

src/test/java/io/frictionlessdata/datapackage/resource/RoundtripTest.java

Lines changed: 168 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,31 +18,15 @@
1818
import java.util.List;
1919
import java.util.stream.Collectors;
2020

21+
import static io.frictionlessdata.datapackage.Profile.PROFILE_TABULAR_DATA_RESOURCE;
22+
2123
/**
2224
* Ensure datapackages are written in a valid format and can be read back. Compare data to see it matches
2325
*/
2426
public class RoundtripTest {
2527
private static final CSVFormat csvFormat = TableDataSource
2628
.getDefaultCsvFormat().builder().setDelimiter('\t').get();
2729

28-
private static final String resourceContent = "[\n" +
29-
" {\n" +
30-
"\t \"city\": \"london\",\n" +
31-
"\t \"year\": 2017,\n" +
32-
"\t \"population\": 8780000\n" +
33-
"\t},\n" +
34-
"\t{\n" +
35-
"\t \"city\": \"paris\",\n" +
36-
"\t \"year\": 2017,\n" +
37-
"\t \"population\": 2240000\n" +
38-
"\t},\n" +
39-
"\t{\n" +
40-
"\t \"city\": \"rome\",\n" +
41-
"\t \"year\": 2017,\n" +
42-
"\t \"population\": 2860000\n" +
43-
"\t}\n" +
44-
" ]";
45-
4630
@Test
4731
@DisplayName("Roundtrip test - write datapackage, read again and compare data")
4832
// this lead to the discovery of https://github.com/frictionlessdata/specs/issues/666, just in case
@@ -87,16 +71,73 @@ public void dogfoodingTest() throws Exception {
8771
void validateResourceRoundtrip() throws Exception {
8872
Path resourcePath = TestUtil.getResourcePath("/fixtures/datapackages/roundtrip/datapackage.json");
8973
Package dp = new Package(resourcePath, true);
90-
StringBuffer buf = new StringBuffer();
91-
Resource v4Resource = dp.getResource("test2");
92-
List<Object[]> testData = v4Resource.getData(false, false, true, false);
93-
String data = createCSV(v4Resource.getHeaders(), testData);
94-
Resource v5Resource = new CSVDataResource(v4Resource.getName(),data);
95-
v5Resource.setDescription(v4Resource.getDescription());
96-
v5Resource.setSchema(v4Resource.getSchema());
97-
v5Resource.setSerializationFormat(Resource.FORMAT_CSV);
98-
List data1 = v5Resource.getData(false, false, true, false);
99-
Assertions.assertArrayEquals(testData.toArray(), data1.toArray());
74+
Resource referenceResource = dp.getResource("test2");
75+
List<Object[]> referenceData = referenceResource.getData(false, false, true, false);
76+
String data = createCSV(referenceResource.getHeaders(), referenceData);
77+
Resource newResource = new CSVDataResource(referenceResource.getName(),data);
78+
newResource.setDescription(referenceResource.getDescription());
79+
newResource.setSchema(referenceResource.getSchema());
80+
newResource.setSerializationFormat(Resource.FORMAT_CSV);
81+
List testData = newResource.getData(false, false, true, false);
82+
Assertions.assertArrayEquals(referenceData.toArray(), testData.toArray());
83+
}
84+
85+
@Test
86+
@DisplayName("Create data resource, compare descriptor")
87+
void validateCreateResourceDescriptorRoundtrip() throws Exception {
88+
Path resourcePath = TestUtil.getResourcePath("/fixtures/datapackages/roundtrip/datapackage.json");
89+
Package pkg = new Package(resourcePath, true);
90+
JSONDataResource resource = new JSONDataResource("test3", resourceContent);
91+
resource.setSchema(Schema.fromJson(
92+
new File(TestUtil.getBasePath().toFile(), "/schema/population_schema.json"), true));
93+
94+
resource.setShouldSerializeToFile(false);
95+
resource.setSerializationFormat(Resource.FORMAT_CSV);
96+
resource.setDialect(Dialect.fromCsvFormat(csvFormat));
97+
resource.setProfile(PROFILE_TABULAR_DATA_RESOURCE);
98+
resource.setEncoding("utf-8");
99+
pkg.addResource(resource);
100+
Path tempDirPath = Files.createTempDirectory("datapackage-");
101+
File createdFile = new File(tempDirPath.toFile(), "test_save_datapackage.zip");
102+
pkg.write(createdFile, true);
103+
System.out.println(tempDirPath);
104+
105+
// create new Package from the serialized form and check they are equal
106+
Package testPkg = new Package(createdFile.toPath(), true);
107+
String json = testPkg.asJson();
108+
Assertions.assertEquals(
109+
descriptorContentInlined.replaceAll("[\n\r]+", "\n"),
110+
json.replaceAll("[\n\r]+", "\n")
111+
);
112+
}
113+
114+
115+
@Test
116+
@DisplayName("Create data resource and make it write to file, compare descriptor")
117+
void validateCreateResourceDescriptorRoundtrip2() throws Exception {
118+
Path resourcePath = TestUtil.getResourcePath("/fixtures/datapackages/roundtrip/datapackage.json");
119+
Package pkg = new Package(resourcePath, true);
120+
JSONDataResource resource = new JSONDataResource("test3", resourceContent);
121+
resource.setSchema(Schema.fromJson(
122+
new File(TestUtil.getBasePath().toFile(), "/schema/population_schema.json"), true));
123+
124+
resource.setShouldSerializeToFile(true);
125+
resource.setSerializationFormat(Resource.FORMAT_CSV);
126+
resource.setDialect(Dialect.fromCsvFormat(csvFormat));
127+
resource.setProfile(PROFILE_TABULAR_DATA_RESOURCE);
128+
resource.setEncoding("utf-8");
129+
pkg.addResource(resource);
130+
Path tempDirPath = Files.createTempDirectory("datapackage-");
131+
File createdFile = new File(tempDirPath.toFile(), "test_save_datapackage.zip");
132+
pkg.write(createdFile, true);
133+
134+
// create new Package from the serialized form and check they are equal
135+
Package testPkg = new Package(createdFile.toPath(), true);
136+
String json = testPkg.asJson();
137+
Assertions.assertEquals(
138+
descriptorContent.replaceAll("[\n\r]+", "\n"),
139+
json.replaceAll("[\n\r]+", "\n")
140+
);
100141
}
101142

102143
private static String createCSV(String[] headers, List<Object[]> data) {
@@ -111,4 +152,103 @@ private static String createCSV(String[] headers, List<Object[]> data) {
111152
}
112153
return sb.toString();
113154
}
155+
156+
157+
private static final String resourceContent = "[\n" +
158+
" {\n" +
159+
"\t \"city\": \"london\",\n" +
160+
"\t \"year\": 2017,\n" +
161+
"\t \"population\": 8780000\n" +
162+
"\t},\n" +
163+
"\t{\n" +
164+
"\t \"city\": \"paris\",\n" +
165+
"\t \"year\": 2017,\n" +
166+
"\t \"population\": 2240000\n" +
167+
"\t},\n" +
168+
"\t{\n" +
169+
"\t \"city\": \"rome\",\n" +
170+
"\t \"year\": 2017,\n" +
171+
"\t \"population\": 2860000\n" +
172+
"\t}\n" +
173+
" ]";
174+
175+
private static final String descriptorContent = "{\n" +
176+
" \"name\" : \"foreign-keys\",\n" +
177+
" \"profile\" : \"data-package\",\n" +
178+
" \"resources\" : [ {\n" +
179+
" \"name\" : \"test2\",\n" +
180+
" \"profile\" : \"data-resource\",\n" +
181+
" \"schema\" : \"schema/test2.json\",\n" +
182+
" \"path\" : \"data/test2.csv\"\n" +
183+
" }, {\n" +
184+
" \"name\" : \"test3\",\n" +
185+
" \"profile\" : \"tabular-data-resource\",\n" +
186+
" \"encoding\" : \"utf-8\",\n" +
187+
" \"dialect\" : \"dialect/test3.json\",\n" +
188+
" \"schema\" : \"schema/population_schema.json\",\n" +
189+
" \"path\" : \"data/test3.csv\"\n" +
190+
" } ]\n" +
191+
"}";
192+
193+
194+
private static String descriptorContentInlined ="{\n" +
195+
" \"name\" : \"foreign-keys\",\n" +
196+
" \"profile\" : \"data-package\",\n" +
197+
" \"resources\" : [ {\n" +
198+
" \"name\" : \"test2\",\n" +
199+
" \"profile\" : \"data-resource\",\n" +
200+
" \"schema\" : \"schema/test2.json\",\n" +
201+
" \"path\" : \"data/test2.csv\"\n" +
202+
" }, {\n" +
203+
" \"name\" : \"test3\",\n" +
204+
" \"profile\" : \"tabular-data-resource\",\n" +
205+
" \"encoding\" : \"utf-8\",\n" +
206+
" \"format\" : \"json\",\n" +
207+
" \"dialect\" : {\n" +
208+
" \"caseSensitiveHeader\" : false,\n" +
209+
" \"quoteChar\" : \"\\\"\",\n" +
210+
" \"doubleQuote\" : true,\n" +
211+
" \"delimiter\" : \"\\t\",\n" +
212+
" \"lineTerminator\" : \"\\r\\n\",\n" +
213+
" \"nullSequence\" : \"\",\n" +
214+
" \"header\" : true,\n" +
215+
" \"csvddfVersion\" : 1.2,\n" +
216+
" \"skipInitialSpace\" : true\n" +
217+
" },\n" +
218+
" \"schema\" : {\n" +
219+
" \"fields\" : [ {\n" +
220+
" \"name\" : \"city\",\n" +
221+
" \"title\" : \"city\",\n" +
222+
" \"type\" : \"string\",\n" +
223+
" \"format\" : \"default\",\n" +
224+
" \"description\" : \"The city.\"\n" +
225+
" }, {\n" +
226+
" \"name\" : \"year\",\n" +
227+
" \"title\" : \"year\",\n" +
228+
" \"type\" : \"year\",\n" +
229+
" \"format\" : \"default\",\n" +
230+
" \"description\" : \"The year.\"\n" +
231+
" }, {\n" +
232+
" \"name\" : \"population\",\n" +
233+
" \"title\" : \"population\",\n" +
234+
" \"type\" : \"integer\",\n" +
235+
" \"format\" : \"default\",\n" +
236+
" \"description\" : \"The population.\"\n" +
237+
" } ]\n" +
238+
" },\n" +
239+
" \"data\" : [ {\n" +
240+
" \"city\" : \"london\",\n" +
241+
" \"year\" : 2017,\n" +
242+
" \"population\" : 8780000\n" +
243+
" }, {\n" +
244+
" \"city\" : \"paris\",\n" +
245+
" \"year\" : 2017,\n" +
246+
" \"population\" : 2240000\n" +
247+
" }, {\n" +
248+
" \"city\" : \"rome\",\n" +
249+
" \"year\" : 2017,\n" +
250+
" \"population\" : 2860000\n" +
251+
" } ]\n" +
252+
" } ]\n" +
253+
"}";
114254
}

0 commit comments

Comments
 (0)