Skip to content

Commit be236ae

Browse files
Merge pull request #32 from OwnYourData/object-generalization
Changing datatype of data fetching to RDFNode
2 parents 9c92662 + 48f9364 commit be236ae

20 files changed

Lines changed: 538 additions & 248 deletions

src/main/java/com/example/anonymization/AnonymizationRestController.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.example.anonymization.dto.AnonymizationJsonLDRequestDto;
55
import com.example.anonymization.entities.Configuration;
66
import com.example.anonymization.service.AnonymizationService;
7+
import com.fasterxml.jackson.core.JsonProcessingException;
78
import io.swagger.v3.oas.annotations.Operation;
89
import io.swagger.v3.oas.annotations.media.Content;
910
import io.swagger.v3.oas.annotations.media.ExampleObject;
@@ -46,6 +47,11 @@ public class AnonymizationRestController {
4647
name = "JSON-LD with two anonymization objects",
4748
summary = "JSON-LD request with two anonymization objects",
4849
externalValue = "/examples/anonymization-request-two-objects.json"
50+
),
51+
@ExampleObject(
52+
name = "JSON-LD of object anonymization (address)",
53+
summary = "JSON-LD request for the anonymization of address objects",
54+
externalValue = "/examples/anonymization-request-object.json"
4955
)
5056
}
5157
)
@@ -55,7 +61,6 @@ public class AnonymizationRestController {
5561
consumes = {"application/json", "application/ld+json"},
5662
produces = "application/json")
5763
public ResponseEntity<String> anonymization(@RequestBody AnonymizationJsonLDRequestDto anonymizationRequest) {
58-
// logger and service call as in your code
5964
return AnonymizationService.applyAnonymization(anonymizationRequest);
6065
}
6166

@@ -89,10 +94,10 @@ public ResponseEntity<String> anonymization(@RequestBody AnonymizationJsonLDRequ
8994
)
9095
)
9196
@PutMapping("/api/anonymization/flatjson")
92-
public ResponseEntity<String> anonymizationFlat(@RequestBody AnonymizationFlatJsonRequestDto anonymizationRequest) {
93-
94-
logger.info("Received flat JSON anonymization request. Body: \n" + anonymizationRequest);
95-
97+
public ResponseEntity<String> anonymizationFlat(
98+
@RequestBody AnonymizationFlatJsonRequestDto anonymizationRequest
99+
) throws JsonProcessingException {
100+
logger.info("Received flat JSON anonymization request. Body: \n{}", anonymizationRequest);
96101
return AnonymizationService.applyAnonymizationFlatJson(anonymizationRequest);
97102
}
98103

src/main/java/com/example/anonymization/GlobalExceptionHandler.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.example.anonymization.exceptions.AnonymizationException;
44
import com.example.anonymization.exceptions.OntologyException;
55
import com.example.anonymization.exceptions.RequestModelException;
6+
import com.fasterxml.jackson.core.JsonProcessingException;
67
import org.springframework.http.HttpStatus;
78
import org.springframework.http.ProblemDetail;
89
import org.springframework.http.ResponseEntity;
@@ -20,7 +21,7 @@ public ResponseEntity<ProblemDetail> handleNotFound(OntologyException ex) {
2021
ProblemDetail pd = ProblemDetail.forStatus(HttpStatus.NOT_FOUND);
2122
pd.setTitle("Error in ontology fetching or parsing");
2223
pd.setDetail(ex.getMessage());
23-
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(pd);
24+
return ResponseEntity.status(pd.getStatus()).body(pd);
2425
}
2526

2627
@ExceptionHandler(AnonymizationException.class)
@@ -29,7 +30,7 @@ public ResponseEntity<ProblemDetail> handleAnonymizationException(AnonymizationE
2930
ProblemDetail pd = ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR);
3031
pd.setTitle("Error during anonymization process");
3132
pd.setDetail(ex.getMessage());
32-
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(pd);
33+
return ResponseEntity.status(pd.getStatus()).body(pd);
3334
}
3435

3536
@ExceptionHandler(RequestModelException.class)
@@ -38,6 +39,15 @@ public ResponseEntity<ProblemDetail> handleRequestModelException(RequestModelExc
3839
ProblemDetail pd = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST);
3940
pd.setTitle("Invalid request model");
4041
pd.setDetail(ex.getMessage());
41-
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(pd);
42+
return ResponseEntity.status(pd.getStatus()).body(pd);
43+
}
44+
45+
@ExceptionHandler(JsonProcessingException.class)
46+
public ResponseEntity<ProblemDetail> handleJsonException(JsonProcessingException ex) {
47+
log.error("JsonProcessingException: {}", ex.getMessage());
48+
ProblemDetail pd = ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR);
49+
pd.setTitle("Error creation Json output");
50+
pd.setDetail(ex.getMessage());
51+
return ResponseEntity.status(pd.getStatus()).body(pd);
4252
}
4353
}

src/main/java/com/example/anonymization/data/QueryBuildingService.java

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ static String createConfigQuery() {
2222
?property rdfs:domain ?anonymizationObject .
2323
?property rdfs:range ?datatype .
2424
?property <https://w3id.org/soya/ns#classification> ?anonymization .
25+
FILTER(isLiteral(?anonymization))
2526
}
2627
""";
2728
}
@@ -37,14 +38,53 @@ static ParameterizedSparqlString createDataModelQuery(
3738
q.append(" ?object a <" + anonymizationObject.getURI()+ ">.\n");
3839
for (Property p : properties) {
3940
String local = p.getLocalName();
40-
q.append(" OPTIONAL { ?object ?" + local + " ?_" + local + ".\n");
41-
q.append("FILTER(isLiteral(?_" + local + ")) }\n");
41+
q.append(" OPTIONAL { ?object ?" + local + " ?_" + local + ". }\n");
4242
q.setParam(local, p);
4343
}
4444
q.append("}");
4545
return q;
4646
}
4747

48+
static ParameterizedSparqlString createAttributeOrderQuery(Resource attribute) {
49+
/*
50+
SELECT ?pos ?value
51+
WHERE {
52+
# You know this subject (replace _:b0 with your actual subject IRI/variable)
53+
_:b0 soya:attributeOrder ?head .
54+
55+
# Each cell of the RDF list and its value
56+
?head rdf:rest* ?cell .
57+
?cell rdf:first ?value .
58+
59+
# Compute the position of each cell in the list
60+
{
61+
SELECT ?cell (COUNT(?mid) AS ?pos)
62+
WHERE {
63+
# ?head is taken from the outer query (correlated subquery)
64+
?head rdf:rest* ?mid .
65+
?mid rdf:rest* ?cell .
66+
}
67+
GROUP BY ?cell
68+
}
69+
}
70+
ORDER BY ?pos
71+
*/
72+
String queryString = """
73+
PREFIX soya: <https://w3id.org/soya/ns#>
74+
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
75+
SELECT ?value
76+
WHERE {
77+
?attribute soya:classification ?o .
78+
?o soya:attributeOrder ?head .
79+
?head rdf:rest* ?cell .
80+
?cell rdf:first ?value .
81+
}
82+
""";
83+
ParameterizedSparqlString q = new ParameterizedSparqlString(queryString);
84+
q.setParam("attribute", attribute);
85+
return q;
86+
}
87+
4888

4989
static ParameterizedSparqlString createKpiDataQuery(Set<Property> properties, Resource kpiObject) {
5090
ParameterizedSparqlString queryString = new ParameterizedSparqlString();
@@ -83,12 +123,14 @@ static ParameterizedSparqlString deleteOriginalPropertyQuery(Set<Property> prope
83123
queryString.append("DELETE {\n");
84124
for (int i = 0; i < properties.size(); i++) {
85125
queryString.append(" ?object ?p" + i + " ?v" + i + " .\n");
126+
queryString.append(" ?v" + i + " ?p ?o . \n");
86127
}
87128
queryString.append("}\nWHERE {\n");
88129
queryString.append(" ?object a ?type .\n");
89130
int i = 0;
90131
for (Property property : properties) {
91-
queryString.append(" OPTIONAL { ?object ?p" + i + " ?v" + i + " . }\n");
132+
queryString.append(" OPTIONAL { ?object ?p" + i + " ?v" + i + " .\n");
133+
queryString.append(" ?v" + i + " ?p ?o . } \n");
92134
queryString.setParam("p" + i, property);
93135
i++;
94136
}

src/main/java/com/example/anonymization/data/QueryService.java

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public static List<ConfigurationResult> getConfigurations(Model model) {
2828
try (QueryExecution qexec = QueryExecutionFactory.create(QueryBuildingService.createConfigQuery(), model)) {
2929
ResultSet rs = qexec.execSelect();
3030
while (rs.hasNext()) {
31+
// TODO maybe include check for Literal in query
3132
QuerySolution solution = rs.nextSolution();
3233
configurations.add(new ConfigurationResult(
3334
solution.getResource("?anonymizationObject"),
@@ -40,27 +41,40 @@ public static List<ConfigurationResult> getConfigurations(Model model) {
4041
return configurations;
4142
}
4243

44+
public static List<String> getAttributeOrder(Model model, Resource attribute) {
45+
List<String> attributes = new ArrayList<>();
46+
try (QueryExecution qexec = QueryExecutionFactory
47+
.create(QueryBuildingService.createAttributeOrderQuery(attribute).asQuery(), model)) {
48+
ResultSet rs = qexec.execSelect();
49+
while (rs.hasNext()) {
50+
QuerySolution solution = rs.nextSolution();
51+
attributes.add(solution.get("?value").toString());
52+
}
53+
}
54+
return attributes;
55+
}
56+
4357
/**
4458
* Extracts the data for a given set of attributes and an object type
4559
* @param model the input model
4660
* @param properties attributes for which data should be fetched
4761
* @param objectType the type for which data should be fetched
4862
* @return mapping of resources of the object type with their property data
4963
*/
50-
public static Map<Resource, Map<Property, Literal>> getData(
64+
public static Map<Resource, Map<Property, RDFNode>> getData(
5165
Model model,
5266
Collection<Property> properties,
5367
Resource objectType
5468
) {
5569
Query query = QueryBuildingService.createDataModelQuery(properties, objectType).asQuery();
56-
Map<Resource, Map<Property, Literal>> results = new HashMap<>();
70+
Map<Resource, Map<Property, RDFNode>> results = new HashMap<>();
5771
try (QueryExecution qexec = QueryExecutionFactory.create(query, model)) {
5872
ResultSet resultSet = qexec.execSelect();
5973
while(resultSet.hasNext()) {
6074
QuerySolution solution = resultSet.nextSolution();
61-
Map<Property, Literal> propertyValues = new HashMap<>();
75+
Map<Property, RDFNode> propertyValues = new HashMap<>();
6276
properties.forEach(property -> {
63-
Literal value = solution.getLiteral("_" + property.getLocalName());
77+
RDFNode value = solution.get("_" + property.getLocalName());
6478
if (value != null) {
6579
propertyValues.put(property, value);
6680
}
@@ -199,7 +213,7 @@ public static List<Set<Resource>> getGeneralizationGroups(Model model, Resource
199213
* @param objectType the object type for which the data is returned
200214
* @return mapping of resources of the object type with their property data
201215
*/
202-
public static Map<Resource, Map<Property, Literal>> getAllData(Model model, Resource objectType) {
216+
public static Map<Resource, Map<Property, RDFNode>> getAllData(Model model, Resource objectType) {
203217
Set<Property> properties = new HashSet<>();
204218
ParameterizedSparqlString pss = QueryBuildingService.createPropertyQuery(objectType);
205219
try (QueryExecution qexec = QueryExecutionFactory.create(pss.asQuery(), model)) {
@@ -305,7 +319,7 @@ public static Map<Resource, List<QueryService.AttributeInformation>> getAttribut
305319
ParameterizedSparqlString queryString = QueryBuildingService.createAttributeInformationQuery(
306320
objectTypes.stream().map(o -> model.getResource(KPI_OBJECT_URI + o.getLocalName())).toList(),
307321
model.createProperty(HAS_ATTRIBUTE_URI),
308-
model.createProperty(NR_ATTRIBUTES_URI),
322+
model.createProperty(NR_BUCKETS_URI),
309323
model.createProperty(ANONYMIZATION_TYP_URI)
310324
);
311325
Query query = queryString.asQuery();
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,48 @@
11
package com.example.anonymization.entities;
22

33

4+
import com.example.anonymization.service.anonymizer.*;
45
import lombok.AllArgsConstructor;
56
import lombok.Data;
67
import lombok.Getter;
8+
import org.apache.jena.rdf.model.Model;
9+
import org.apache.jena.rdf.model.Property;
10+
import org.apache.jena.rdf.model.RDFNode;
11+
import org.apache.jena.rdf.model.Resource;
12+
13+
import java.util.Map;
714

815
@Data
916
@Getter
1017
@AllArgsConstructor
1118
public class Configuration {
1219
String dataType;
1320
String anonymization;
21+
22+
public Anonymization<? extends Configuration> createAnonymization(
23+
Model model,
24+
Property property,
25+
Map<Resource, RDFNode> data,
26+
int nrAttr,
27+
Resource anonymizationObject
28+
) {
29+
return switch (anonymization) {
30+
case "generalization" -> switch (dataType) {
31+
case "integer", "double" -> new GeneralizationNumeric(model, property, data, nrAttr, this, anonymizationObject);
32+
case "date" -> new GeneralizationDate(model, property, data, nrAttr, this, anonymizationObject);
33+
case "string" -> throw new IllegalArgumentException("No Generalization possible for type string");
34+
default ->
35+
throw new IllegalArgumentException("Invalid configuration type for object anonymization");
36+
};
37+
case "randomization" -> switch (dataType) {
38+
case "integer", "double" -> new RandomizationNumeric(model, property, data, nrAttr, this, anonymizationObject);
39+
case "date" -> new RandomizationDate(model, property, data, nrAttr, this, anonymizationObject);
40+
default ->
41+
throw new IllegalArgumentException("No Randomization possible for type " + dataType);
42+
};
43+
case "masking" -> new Masking(model, property, data, this, anonymizationObject);
44+
default ->
45+
throw new IllegalArgumentException("No Anonymization implementation for " + anonymization + ": " + dataType);
46+
};
47+
}
1448
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.example.anonymization.entities;
2+
3+
import com.example.anonymization.service.anonymizer.GeneralizationObject;
4+
import lombok.Getter;
5+
import org.apache.jena.rdf.model.Model;
6+
import org.apache.jena.rdf.model.Property;
7+
import org.apache.jena.rdf.model.RDFNode;
8+
import org.apache.jena.rdf.model.Resource;
9+
10+
import java.util.List;
11+
import java.util.Map;
12+
13+
@Getter
14+
public class ObjectGeneralizationConfig extends Configuration {
15+
16+
List<String> attributeOrder;
17+
18+
public ObjectGeneralizationConfig(String dataType, List<String> attributeOrder) {
19+
super(dataType, "generalization");
20+
this.attributeOrder = attributeOrder;
21+
}
22+
23+
@Override
24+
public GeneralizationObject createAnonymization(
25+
Model model,
26+
Property property,
27+
Map<Resource, RDFNode> data,
28+
int nrAttr,
29+
Resource anonymizationObject
30+
) {
31+
return new GeneralizationObject(model, property, data, nrAttr, this, anonymizationObject);
32+
}
33+
}

0 commit comments

Comments
 (0)