Skip to content

Commit

Permalink
feat: adding pairwise (#98)
Browse files Browse the repository at this point in the history
* Correct pom

* Upgrade to junit 5

* Remove unused and dangerous xalan dependency

* upgrading version

* adding pairwise

* changing Pairwise into PairwiseLinks

* Changing Pairwise into PairwiseLinks

* adding symLinks for pairwise

* fix single item in links list

* add maxOccurs in symLinks

* add pairwise test file

* Create PairwiseTest.java

* add `symLinksCleaning` method in cleaning class

* (fix) add out test folder

* same test in one go

* remove file writing in PairwiseTest

* update pairwise test file

* generify symLinks entry type in cleaning

* (fix) PairwiseLinks xAxis single attribute

* (fix) single symLinks attribute in PairwiseLinks

update symLinks cleaning

* change symLinks type to string

* bump version

* reduce "symLinksCleaning" cognitive complexity

* refactor SymLinks cleaning in separate class

* create test class for SymLinks cleaning

* null case management

* add 'no validation' warn in javadoc

* reorder string.equals(...) usages

* try with resource for jsonReader

* sandbox test on JsonObject getString method

* restore component list in PairwiseLinks

* bump version

* fix: superfluous quote marks in symLinks cleaning

* update symlinks

* synchronize symlinks

* test: update pairwise hierarchical test file

Co-Authored-By: François Bulot <[email protected]>

* test: add flat versions of pairwise test file

* refactor: small enhancement

* feat: add deserialize for input stream

* fix: update "symLinks" with variable name

* test: enhance pairwise tests

* test: better test on PairwiseLinks deserialization

* Feat/serializer for eno3 (#87)

* feat: create serialization exception class

* feat: add second json serialize method

* refactor: utils class in utils package

* test: json utils class

* test: unit tests on json serializers

* build: bump rc version

* feat: adapt suggester rules to new model

* Fix/resizing (#96)

* test: add questionnaire in resources

* style: linter thing

* test: case for the identified issue

* fix: invert cleaning steps solves the issue

* build: bump rc version

* test: refactor pairwise test

* fix: update json-cleaning xslt for pairwise

* Fix/resizing take 2 (#97)

* test: add questionnaire in resources

* style: linter thing

* test: case for the identified issue

* fix: invert cleaning steps solves the issue

* build: bump rc version

* test: refactor pairwise test

* fix: update json-cleaning xslt for pairwise

* chore: bump rc version

* fix: revert "adapt suggester rules to new model"

This reverts commit f481734

* fix: correction for storeName/resizing

---------

Co-authored-by: Fabrice Bibonne <[email protected]>
Co-authored-by: Nicolas Senave <[email protected]>
Co-authored-by: Nicolas Sénave <[email protected]>
Co-authored-by: Bulot François <[email protected]>
Co-authored-by: François Bulot <[email protected]>
Co-authored-by: Benoit Werquin <[email protected]>
Co-authored-by: David Darras <[email protected]>
  • Loading branch information
8 people authored Jun 5, 2023
1 parent 95730d4 commit 38f5404
Show file tree
Hide file tree
Showing 23 changed files with 3,859 additions and 163 deletions.
30 changes: 13 additions & 17 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
<artifactId>lunatic-model</artifactId>
<packaging>jar</packaging>

<version>2.3.1</version>

<version>2.3.2</version>


<name>Lunatic Model</name>
<description>Classes and converters for the Lunatic model</description>
Expand All @@ -16,6 +18,7 @@
<saxon.version>9.7.0-8</saxon.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.release>11</maven.compiler.release>
</properties>

<licenses>
Expand Down Expand Up @@ -48,12 +51,7 @@
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.xmlunit</groupId>
<artifactId>xmlunit-matchers</artifactId>
Expand Down Expand Up @@ -83,22 +81,22 @@
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<pluginManagement>
<plugins>
<plugin>
<!-- Force version of maven compiler plugin-->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<inputEncoding>UTF-8</inputEncoding>
<outputEncoding>UTF-8</outputEncoding>
<argLine>-Dfile.encoding=UTF-8</argLine>
</configuration>
<version>3.10.1</version>
</plugin>
</plugins>
</pluginManagement>
Expand All @@ -109,8 +107,6 @@
<version>2.22.0</version>
<configuration>
<encoding>UTF-8</encoding>
<inputEncoding>UTF-8</inputEncoding>
<outputEncoding>UTF-8</outputEncoding>
<argLine>-Dfile.encoding=UTF-8
-Djavax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory</argLine>
</configuration>
Expand Down
24 changes: 14 additions & 10 deletions src/main/java/fr/insee/lunatic/conversion/JSONCleaner.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package fr.insee.lunatic.conversion;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import fr.insee.lunatic.Constants;
import fr.insee.lunatic.utils.XslTransformation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import fr.insee.lunatic.Constants;
import fr.insee.lunatic.utils.XslTransformation;
import java.io.*;
import java.nio.charset.StandardCharsets;

/**
* remove technical attribute as xsi:type
Expand All @@ -19,7 +16,7 @@
*/
public class JSONCleaner {

private static XslTransformation saxonService = new XslTransformation();
private static final XslTransformation saxonService = new XslTransformation();

private static final Logger logger = LoggerFactory.getLogger(JSONCleaner.class);

Expand All @@ -28,15 +25,21 @@ public String clean(String jsonString) throws Exception {

if ((jsonString == null) || (jsonString.length() == 0))
return null;
InputStream json = new ByteArrayInputStream(wrapJsonWithXml(jsonString).getBytes("UTF-8"));
InputStream json = new ByteArrayInputStream(wrapJsonWithXml(jsonString).getBytes(StandardCharsets.UTF_8));

// Unchanged step: use XSLT "cleaning" sheet
String generatedUsingXslt = generate(json);

return this.generate(json);
// New step: apply (Java) symLinks cleaning
JSONSymLinksCleaner jsonSymLinksCleaner = new JSONSymLinksCleaner();
return jsonSymLinksCleaner.clean(generatedUsingXslt);
}

public String generate(InputStream isFinalInput) throws Exception {
OutputStream osOutputFile = generateOS(isFinalInput);
String res = osOutputFile.toString();
osOutputFile.close();

return res;
}

Expand All @@ -63,4 +66,5 @@ public String preProcessJson2XML(String json) {
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;");
}

}
31 changes: 25 additions & 6 deletions src/main/java/fr/insee/lunatic/conversion/JSONDeserializer.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package fr.insee.lunatic.conversion;

import fr.insee.lunatic.model.flat.Questionnaire;
import org.eclipse.persistence.jaxb.UnmarshallerProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.eclipse.persistence.jaxb.UnmarshallerProperties;

import fr.insee.lunatic.model.flat.Questionnaire;
import java.io.InputStream;

public class JSONDeserializer {

Expand All @@ -36,4 +36,23 @@ public Questionnaire deserialize(String fileName) throws JAXBException {
return questionnaire;
}

public Questionnaire deserialize(InputStream jsonQuestionnaire) throws JAXBException {
if (jsonQuestionnaire == null) return null;

logger.debug("Deserializing questionnaire from input stream");

JAXBContext context = JAXBContext.newInstance(Questionnaire.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, "application/json");
unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false);

StreamSource json = new StreamSource(jsonQuestionnaire);
Questionnaire questionnaire = unmarshaller.unmarshal(json, Questionnaire.class).getValue();

logger.debug("Questionnaire " + questionnaire.getId() + " successfully deserialized");

return questionnaire;
}


}
49 changes: 40 additions & 9 deletions src/main/java/fr/insee/lunatic/conversion/JSONSerializer.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
package fr.insee.lunatic.conversion;

import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import fr.insee.lunatic.exception.SerializationException;
import fr.insee.lunatic.model.flat.Questionnaire;
import org.eclipse.persistence.jaxb.MarshallerProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.JsonWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.eclipse.persistence.jaxb.MarshallerProperties;

import fr.insee.lunatic.model.flat.Questionnaire;
import java.io.*;
import java.nio.charset.StandardCharsets;

public class JSONSerializer {

Expand All @@ -37,8 +40,36 @@ public String serialize(Questionnaire questionnaire) throws JAXBException, Unsup

ByteArrayOutputStream baos = new ByteArrayOutputStream();
marshaller.marshal(questionnaire, baos);

return baos.toString("UTF-8");

}

/** <code>serialize</code> method returns a json string beginning with {"Questionnaire": {...}}.
* The need is to actually have questionnaire content directly at the root of the json tree.
* This method returns the json content of the given questionnaire without the "Questionnaire" level.
* @param questionnaire Lunatic questionnaire object (flat model).
* @return The questionnaire as a json string.
*/
public String serialize2(Questionnaire questionnaire) throws SerializationException {
//
String questionnaireString;
try {
questionnaireString = serialize(questionnaire);
} catch (JAXBException | UnsupportedEncodingException e) {
throw new SerializationException("Error when calling first serialize method.", e);
}
//
try (JsonReader jsonReader = Json.createReader(
new ByteArrayInputStream(questionnaireString.getBytes(StandardCharsets.UTF_8)));
OutputStream outputStream = new ByteArrayOutputStream();
JsonWriter jsonWriter = Json.createWriter(outputStream)) {
JsonObject questionnaireJson = jsonReader.readObject();
JsonObject contentJson = questionnaireJson.getJsonObject("Questionnaire");
jsonWriter.writeObject(contentJson);
return outputStream.toString();
} catch (IOException e) {
throw new SerializationException("Error when removing \"Questionnaire\" level of given questionnaire.", e);
}
}
}
102 changes: 102 additions & 0 deletions src/main/java/fr/insee/lunatic/conversion/JSONSymLinksCleaner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package fr.insee.lunatic.conversion;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.json.*;
import java.io.*;
import java.nio.charset.StandardCharsets;

public class JSONSymLinksCleaner {

private static final Logger logger = LoggerFactory.getLogger(JSONSymLinksCleaner.class);

/**
* Given a Lunatic JSON flat questionnaire, replace the source/target fields by key/values in "symLinks" attribute
* in "PairwiseLinks" components to be compliant with the Lunatic JS library.
* Warning: no validation is done on the string input. */
public String clean(String stringFlatQuestionnaire) throws IOException {

if (stringFlatQuestionnaire == null) {
logger.warn("null string given in JSON SymLinks cleaner.");
return null;
}

// Read the json string into a JsonObject
try (JsonReader jsonReader = Json.createReader(
new ByteArrayInputStream(stringFlatQuestionnaire.getBytes(StandardCharsets.UTF_8)))) {
JsonObject jsonQuestionnaire = jsonReader.readObject();

// We will copy the entire input json object, except the "symLinks" attribute in PairwiseLinks components
JsonObjectBuilder jsonQuestionnaireBuilder = Json.createObjectBuilder();
editQuestionnaire(jsonQuestionnaire, jsonQuestionnaireBuilder);

OutputStream outputStream = new ByteArrayOutputStream();

JsonWriter jsonWriter = Json.createWriter(outputStream);
jsonWriter.writeObject(jsonQuestionnaireBuilder.build());

String result = outputStream.toString();
outputStream.close();

return result;
}

}

private static void editQuestionnaire(JsonObject jsonQuestionnaire, JsonObjectBuilder jsonQuestionnaireBuilder) {
jsonQuestionnaire.forEach((key, jsonValue) -> {
if (! "components".equals(key)) {
jsonQuestionnaireBuilder.add(key, jsonValue);
} else {
editComponents(jsonQuestionnaireBuilder, (JsonArray) jsonValue);
}
});
}

private static void editComponents(JsonObjectBuilder jsonQuestionnaireBuilder, JsonArray jsonComponents) {
JsonArrayBuilder jsonComponentsBuilder = Json.createArrayBuilder();
for (JsonValue jsonValue1 : jsonComponents) {
JsonObject jsonComponent = (JsonObject) jsonValue1;
if (! "PairwiseLinks".equals(jsonComponent.getString("componentType"))) {
jsonComponentsBuilder.add(jsonValue1);
} else {
editPairwiseLinks(jsonComponentsBuilder, (JsonObject) jsonValue1);
}
}
jsonQuestionnaireBuilder.add("components", jsonComponentsBuilder.build());
}

private static void editPairwiseLinks(JsonArrayBuilder jsonComponentsBuilder, JsonObject jsonPairwiseLinks) {
JsonObjectBuilder jsonPairwiseBuilder = Json.createObjectBuilder();
jsonPairwiseLinks.forEach((key2, jsonValue2) -> {
if (! "symLinks".equals(key2)) {
jsonPairwiseBuilder.add(key2, jsonValue2);
} else {
editSymLinks(jsonPairwiseBuilder, (JsonObject) jsonValue2);
}
});
jsonComponentsBuilder.add(jsonPairwiseBuilder.build());
}

private static void editSymLinks(JsonObjectBuilder jsonPairwiseBuilder, JsonObject jsonSymLinks) {
JsonObjectBuilder jsonSymLinksBuilder = Json.createObjectBuilder();
JsonObjectBuilder jsonLINKSBuilder = Json.createObjectBuilder();
String symLinksName = jsonSymLinks.getJsonString("name").getString();
jsonSymLinks.getJsonArray("LINK").forEach(jsonValue3 -> {
JsonObject jsonSourceTarget = (JsonObject) jsonValue3;
JsonString sourceKey = (JsonString) jsonSourceTarget.get("source");
JsonString targetKey = (JsonString) jsonSourceTarget.get("target");
// target field is not mandatory and can be null
// json converted from xml can contain "null" string values
if (targetKey != null && !"null".equals(targetKey.getString())) {
jsonLINKSBuilder.add(sourceKey.getString(), targetKey);
} else {
jsonLINKSBuilder.addNull(sourceKey.getString());
}
});
jsonSymLinksBuilder.add(symLinksName, jsonLINKSBuilder.build());
jsonPairwiseBuilder.add("symLinks", jsonSymLinksBuilder.build());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package fr.insee.lunatic.exception;

/** Thrown when an error occurs during serialization of a questionnaire object. */
public class SerializationException extends Exception {

public SerializationException(String message, Exception e) {
super(message, e);
}

}
Loading

0 comments on commit 38f5404

Please sign in to comment.