From 48f93648ad1a6cc1234a94861ec76afd32463115 Mon Sep 17 00:00:00 2001 From: Paul Feichtenschlager Date: Wed, 26 Nov 2025 07:56:47 +0100 Subject: [PATCH] Changing datatype of data fetching to RDFNode --- .../AnonymizationRestController.java | 15 ++- .../anonymization/GlobalExceptionHandler.java | 16 ++- .../data/QueryBuildingService.java | 48 ++++++- .../anonymization/data/QueryService.java | 26 +++- .../anonymization/entities/Configuration.java | 34 +++++ .../entities/ObjectGeneralizationConfig.java | 33 +++++ .../service/AnonymizationService.java | 40 +++--- .../service/ConfigurationService.java | 82 ++++++++---- .../service/FaltJsonService.java | 122 ++++++++++-------- .../anonymization/service/KpiService.java | 46 +++---- .../service/anonymizer/Anonymization.java | 61 +++------ .../service/anonymizer/Generalization.java | 39 ++++-- .../anonymizer/GeneralizationDate.java | 15 +-- .../anonymizer/GeneralizationNumeric.java | 21 +-- .../anonymizer/GeneralizationObject.java | 48 +++++-- .../service/anonymizer/Masking.java | 9 +- .../service/anonymizer/Randomization.java | 36 ++++-- .../service/anonymizer/RandomizationDate.java | 3 +- .../anonymizer/RandomizationNumeric.java | 2 +- .../anonymization-request-object.json | 90 +++++++++++++ 20 files changed, 538 insertions(+), 248 deletions(-) create mode 100644 src/main/java/com/example/anonymization/entities/ObjectGeneralizationConfig.java create mode 100644 src/main/resources/static/examples/anonymization-request-object.json diff --git a/src/main/java/com/example/anonymization/AnonymizationRestController.java b/src/main/java/com/example/anonymization/AnonymizationRestController.java index 664ae85..f440566 100644 --- a/src/main/java/com/example/anonymization/AnonymizationRestController.java +++ b/src/main/java/com/example/anonymization/AnonymizationRestController.java @@ -4,6 +4,7 @@ import com.example.anonymization.dto.AnonymizationJsonLDRequestDto; import com.example.anonymization.entities.Configuration; import com.example.anonymization.service.AnonymizationService; +import com.fasterxml.jackson.core.JsonProcessingException; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.ExampleObject; @@ -46,6 +47,11 @@ public class AnonymizationRestController { name = "JSON-LD with two anonymization objects", summary = "JSON-LD request with two anonymization objects", externalValue = "/examples/anonymization-request-two-objects.json" + ), + @ExampleObject( + name = "JSON-LD of object anonymization (address)", + summary = "JSON-LD request for the anonymization of address objects", + externalValue = "/examples/anonymization-request-object.json" ) } ) @@ -55,7 +61,6 @@ public class AnonymizationRestController { consumes = {"application/json", "application/ld+json"}, produces = "application/json") public ResponseEntity anonymization(@RequestBody AnonymizationJsonLDRequestDto anonymizationRequest) { - // logger and service call as in your code return AnonymizationService.applyAnonymization(anonymizationRequest); } @@ -89,10 +94,10 @@ public ResponseEntity anonymization(@RequestBody AnonymizationJsonLDRequ ) ) @PutMapping("/api/anonymization/flatjson") - public ResponseEntity anonymizationFlat(@RequestBody AnonymizationFlatJsonRequestDto anonymizationRequest) { - - logger.info("Received flat JSON anonymization request. Body: \n" + anonymizationRequest); - + public ResponseEntity anonymizationFlat( + @RequestBody AnonymizationFlatJsonRequestDto anonymizationRequest + ) throws JsonProcessingException { + logger.info("Received flat JSON anonymization request. Body: \n{}", anonymizationRequest); return AnonymizationService.applyAnonymizationFlatJson(anonymizationRequest); } diff --git a/src/main/java/com/example/anonymization/GlobalExceptionHandler.java b/src/main/java/com/example/anonymization/GlobalExceptionHandler.java index 6c23421..ce4c255 100644 --- a/src/main/java/com/example/anonymization/GlobalExceptionHandler.java +++ b/src/main/java/com/example/anonymization/GlobalExceptionHandler.java @@ -3,6 +3,7 @@ import com.example.anonymization.exceptions.AnonymizationException; import com.example.anonymization.exceptions.OntologyException; import com.example.anonymization.exceptions.RequestModelException; +import com.fasterxml.jackson.core.JsonProcessingException; import org.springframework.http.HttpStatus; import org.springframework.http.ProblemDetail; import org.springframework.http.ResponseEntity; @@ -20,7 +21,7 @@ public ResponseEntity handleNotFound(OntologyException ex) { ProblemDetail pd = ProblemDetail.forStatus(HttpStatus.NOT_FOUND); pd.setTitle("Error in ontology fetching or parsing"); pd.setDetail(ex.getMessage()); - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(pd); + return ResponseEntity.status(pd.getStatus()).body(pd); } @ExceptionHandler(AnonymizationException.class) @@ -29,7 +30,7 @@ public ResponseEntity handleAnonymizationException(AnonymizationE ProblemDetail pd = ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR); pd.setTitle("Error during anonymization process"); pd.setDetail(ex.getMessage()); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(pd); + return ResponseEntity.status(pd.getStatus()).body(pd); } @ExceptionHandler(RequestModelException.class) @@ -38,6 +39,15 @@ public ResponseEntity handleRequestModelException(RequestModelExc ProblemDetail pd = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST); pd.setTitle("Invalid request model"); pd.setDetail(ex.getMessage()); - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(pd); + return ResponseEntity.status(pd.getStatus()).body(pd); + } + + @ExceptionHandler(JsonProcessingException.class) + public ResponseEntity handleJsonException(JsonProcessingException ex) { + log.error("JsonProcessingException: {}", ex.getMessage()); + ProblemDetail pd = ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR); + pd.setTitle("Error creation Json output"); + pd.setDetail(ex.getMessage()); + return ResponseEntity.status(pd.getStatus()).body(pd); } } diff --git a/src/main/java/com/example/anonymization/data/QueryBuildingService.java b/src/main/java/com/example/anonymization/data/QueryBuildingService.java index 93fc049..c899df9 100644 --- a/src/main/java/com/example/anonymization/data/QueryBuildingService.java +++ b/src/main/java/com/example/anonymization/data/QueryBuildingService.java @@ -22,6 +22,7 @@ static String createConfigQuery() { ?property rdfs:domain ?anonymizationObject . ?property rdfs:range ?datatype . ?property ?anonymization . + FILTER(isLiteral(?anonymization)) } """; } @@ -37,14 +38,53 @@ static ParameterizedSparqlString createDataModelQuery( q.append(" ?object a <" + anonymizationObject.getURI()+ ">.\n"); for (Property p : properties) { String local = p.getLocalName(); - q.append(" OPTIONAL { ?object ?" + local + " ?_" + local + ".\n"); - q.append("FILTER(isLiteral(?_" + local + ")) }\n"); + q.append(" OPTIONAL { ?object ?" + local + " ?_" + local + ". }\n"); q.setParam(local, p); } q.append("}"); return q; } + static ParameterizedSparqlString createAttributeOrderQuery(Resource attribute) { + /* + SELECT ?pos ?value +WHERE { + # You know this subject (replace _:b0 with your actual subject IRI/variable) + _:b0 soya:attributeOrder ?head . + + # Each cell of the RDF list and its value + ?head rdf:rest* ?cell . + ?cell rdf:first ?value . + + # Compute the position of each cell in the list + { + SELECT ?cell (COUNT(?mid) AS ?pos) + WHERE { + # ?head is taken from the outer query (correlated subquery) + ?head rdf:rest* ?mid . + ?mid rdf:rest* ?cell . + } + GROUP BY ?cell + } +} +ORDER BY ?pos + */ + String queryString = """ + PREFIX soya: + PREFIX rdf: + SELECT ?value + WHERE { + ?attribute soya:classification ?o . + ?o soya:attributeOrder ?head . + ?head rdf:rest* ?cell . + ?cell rdf:first ?value . + } + """; + ParameterizedSparqlString q = new ParameterizedSparqlString(queryString); + q.setParam("attribute", attribute); + return q; + } + static ParameterizedSparqlString createKpiDataQuery(Set properties, Resource kpiObject) { ParameterizedSparqlString queryString = new ParameterizedSparqlString(); @@ -83,12 +123,14 @@ static ParameterizedSparqlString deleteOriginalPropertyQuery(Set prope queryString.append("DELETE {\n"); for (int i = 0; i < properties.size(); i++) { queryString.append(" ?object ?p" + i + " ?v" + i + " .\n"); + queryString.append(" ?v" + i + " ?p ?o . \n"); } queryString.append("}\nWHERE {\n"); queryString.append(" ?object a ?type .\n"); int i = 0; for (Property property : properties) { - queryString.append(" OPTIONAL { ?object ?p" + i + " ?v" + i + " . }\n"); + queryString.append(" OPTIONAL { ?object ?p" + i + " ?v" + i + " .\n"); + queryString.append(" ?v" + i + " ?p ?o . } \n"); queryString.setParam("p" + i, property); i++; } diff --git a/src/main/java/com/example/anonymization/data/QueryService.java b/src/main/java/com/example/anonymization/data/QueryService.java index f6e103b..7edb6d3 100644 --- a/src/main/java/com/example/anonymization/data/QueryService.java +++ b/src/main/java/com/example/anonymization/data/QueryService.java @@ -28,6 +28,7 @@ public static List getConfigurations(Model model) { try (QueryExecution qexec = QueryExecutionFactory.create(QueryBuildingService.createConfigQuery(), model)) { ResultSet rs = qexec.execSelect(); while (rs.hasNext()) { + // TODO maybe include check for Literal in query QuerySolution solution = rs.nextSolution(); configurations.add(new ConfigurationResult( solution.getResource("?anonymizationObject"), @@ -40,6 +41,19 @@ public static List getConfigurations(Model model) { return configurations; } + public static List getAttributeOrder(Model model, Resource attribute) { + List attributes = new ArrayList<>(); + try (QueryExecution qexec = QueryExecutionFactory + .create(QueryBuildingService.createAttributeOrderQuery(attribute).asQuery(), model)) { + ResultSet rs = qexec.execSelect(); + while (rs.hasNext()) { + QuerySolution solution = rs.nextSolution(); + attributes.add(solution.get("?value").toString()); + } + } + return attributes; + } + /** * Extracts the data for a given set of attributes and an object type * @param model the input model @@ -47,20 +61,20 @@ public static List getConfigurations(Model model) { * @param objectType the type for which data should be fetched * @return mapping of resources of the object type with their property data */ - public static Map> getData( + public static Map> getData( Model model, Collection properties, Resource objectType ) { Query query = QueryBuildingService.createDataModelQuery(properties, objectType).asQuery(); - Map> results = new HashMap<>(); + Map> results = new HashMap<>(); try (QueryExecution qexec = QueryExecutionFactory.create(query, model)) { ResultSet resultSet = qexec.execSelect(); while(resultSet.hasNext()) { QuerySolution solution = resultSet.nextSolution(); - Map propertyValues = new HashMap<>(); + Map propertyValues = new HashMap<>(); properties.forEach(property -> { - Literal value = solution.getLiteral("_" + property.getLocalName()); + RDFNode value = solution.get("_" + property.getLocalName()); if (value != null) { propertyValues.put(property, value); } @@ -199,7 +213,7 @@ public static List> getGeneralizationGroups(Model model, Resource * @param objectType the object type for which the data is returned * @return mapping of resources of the object type with their property data */ - public static Map> getAllData(Model model, Resource objectType) { + public static Map> getAllData(Model model, Resource objectType) { Set properties = new HashSet<>(); ParameterizedSparqlString pss = QueryBuildingService.createPropertyQuery(objectType); try (QueryExecution qexec = QueryExecutionFactory.create(pss.asQuery(), model)) { @@ -305,7 +319,7 @@ public static Map> getAttribut ParameterizedSparqlString queryString = QueryBuildingService.createAttributeInformationQuery( objectTypes.stream().map(o -> model.getResource(KPI_OBJECT_URI + o.getLocalName())).toList(), model.createProperty(HAS_ATTRIBUTE_URI), - model.createProperty(NR_ATTRIBUTES_URI), + model.createProperty(NR_BUCKETS_URI), model.createProperty(ANONYMIZATION_TYP_URI) ); Query query = queryString.asQuery(); diff --git a/src/main/java/com/example/anonymization/entities/Configuration.java b/src/main/java/com/example/anonymization/entities/Configuration.java index b56f8ae..d6d35ce 100644 --- a/src/main/java/com/example/anonymization/entities/Configuration.java +++ b/src/main/java/com/example/anonymization/entities/Configuration.java @@ -1,9 +1,16 @@ package com.example.anonymization.entities; +import com.example.anonymization.service.anonymizer.*; import lombok.AllArgsConstructor; import lombok.Data; import lombok.Getter; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.Property; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.rdf.model.Resource; + +import java.util.Map; @Data @Getter @@ -11,4 +18,31 @@ public class Configuration { String dataType; String anonymization; + + public Anonymization createAnonymization( + Model model, + Property property, + Map data, + int nrAttr, + Resource anonymizationObject + ) { + return switch (anonymization) { + case "generalization" -> switch (dataType) { + case "integer", "double" -> new GeneralizationNumeric(model, property, data, nrAttr, this, anonymizationObject); + case "date" -> new GeneralizationDate(model, property, data, nrAttr, this, anonymizationObject); + case "string" -> throw new IllegalArgumentException("No Generalization possible for type string"); + default -> + throw new IllegalArgumentException("Invalid configuration type for object anonymization"); + }; + case "randomization" -> switch (dataType) { + case "integer", "double" -> new RandomizationNumeric(model, property, data, nrAttr, this, anonymizationObject); + case "date" -> new RandomizationDate(model, property, data, nrAttr, this, anonymizationObject); + default -> + throw new IllegalArgumentException("No Randomization possible for type " + dataType); + }; + case "masking" -> new Masking(model, property, data, this, anonymizationObject); + default -> + throw new IllegalArgumentException("No Anonymization implementation for " + anonymization + ": " + dataType); + }; + } } diff --git a/src/main/java/com/example/anonymization/entities/ObjectGeneralizationConfig.java b/src/main/java/com/example/anonymization/entities/ObjectGeneralizationConfig.java new file mode 100644 index 0000000..58dae34 --- /dev/null +++ b/src/main/java/com/example/anonymization/entities/ObjectGeneralizationConfig.java @@ -0,0 +1,33 @@ +package com.example.anonymization.entities; + +import com.example.anonymization.service.anonymizer.GeneralizationObject; +import lombok.Getter; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.Property; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.rdf.model.Resource; + +import java.util.List; +import java.util.Map; + +@Getter +public class ObjectGeneralizationConfig extends Configuration { + + List attributeOrder; + + public ObjectGeneralizationConfig(String dataType, List attributeOrder) { + super(dataType, "generalization"); + this.attributeOrder = attributeOrder; + } + + @Override + public GeneralizationObject createAnonymization( + Model model, + Property property, + Map data, + int nrAttr, + Resource anonymizationObject + ) { + return new GeneralizationObject(model, property, data, nrAttr, this, anonymizationObject); + } +} diff --git a/src/main/java/com/example/anonymization/service/AnonymizationService.java b/src/main/java/com/example/anonymization/service/AnonymizationService.java index 57db626..a4d2966 100644 --- a/src/main/java/com/example/anonymization/service/AnonymizationService.java +++ b/src/main/java/com/example/anonymization/service/AnonymizationService.java @@ -6,6 +6,7 @@ import com.example.anonymization.exceptions.RequestModelException; import com.example.anonymization.service.anonymizer.Anonymization; import com.example.anonymization.data.QueryService; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import org.apache.jena.rdf.model.*; import org.apache.jena.riot.RDFDataMgr; @@ -41,7 +42,9 @@ public static ResponseEntity applyAnonymization(AnonymizationJsonLDReque ); } - public static ResponseEntity applyAnonymizationFlatJson(AnonymizationFlatJsonRequestDto request) { + public static ResponseEntity applyAnonymizationFlatJson( + AnonymizationFlatJsonRequestDto request + ) throws JsonProcessingException { Map configs = ConfigurationService.fetchFlatConfig(request.getConfigurationUrl()); Model model = ModelFactory.createDefaultModel(); FaltJsonService.addDataToFlatModel(model, request.getData(), request.getPrefix()); @@ -50,7 +53,12 @@ public static ResponseEntity applyAnonymizationFlatJson(AnonymizationFla anonymizationObjects.forEach( (o, c) -> applyAnonymizationForObject(o, c, model) ); - String out = FaltJsonService.createFlatJsonOutput(model, configs, anonymizationObjects.keySet(), request.getPrefix()); + String out = FaltJsonService.createFlatJsonOutput( + model, + configs, + anonymizationObjects.keySet(), + request.getPrefix() + ); logger.info(out); return new ResponseEntity<>(out, HttpStatus.ACCEPTED); } @@ -61,29 +69,29 @@ private static void applyAnonymizationForObject( Model model ) { Set attributes = QueryService.getProperties(model, configurations.keySet(), anonymizationObject); - Map> data = QueryService.getData(model, attributes, anonymizationObject); - Map> horizontalData = convertToHorizontalSchema(data, attributes); + Map> data = QueryService.getData(model, attributes, anonymizationObject); + Map> horizontalData = convertToHorizontalSchema(data, attributes); int nrAnonymizeAttributes = getNumberOfAnonymizingAttributes(configurations, attributes); - horizontalData.entrySet().stream().map(e -> Anonymization.anonymizationFactoryFunction( - configurations.get(e.getKey()), - model, - e.getKey(), - e.getValue(), - nrAnonymizeAttributes, - anonymizationObject + horizontalData.entrySet().stream().map(e -> + configurations.get(e.getKey()).createAnonymization( + model, + e.getKey(), + e.getValue(), + nrAnonymizeAttributes, + anonymizationObject )).forEach(Anonymization::anonymization); KpiService.addKpiObject(model, anonymizationObject, attributes, configurations); QueryService.deleteOriginalProperties(model, attributes, anonymizationObject); } - private static Map> convertToHorizontalSchema( - Map> data, + private static Map> convertToHorizontalSchema( + Map> data, Set properties ) { - Map> propertyMap = new HashMap<>(); + Map> propertyMap = new HashMap<>(); properties.forEach(property -> propertyMap.put(property, new HashMap<>())); data.forEach((resource, value) -> value.forEach( - (property, literal) -> propertyMap.get(property).put(resource, literal) + (property, node) -> propertyMap.get(property).put(resource, node) )); return propertyMap; } @@ -106,7 +114,7 @@ private static Model getModel(JsonNode data) { RDFDataMgr.read(model, new StringReader(jsonLdString), null, RDFLanguages.JSONLD); return model; } catch (Exception e) { - throw new RequestModelException("The Request Data coudl not be converted ot a model: " + e.getMessage()); + throw new RequestModelException("The Request Data could not be converted ot a model: " + e.getMessage()); } } } diff --git a/src/main/java/com/example/anonymization/service/ConfigurationService.java b/src/main/java/com/example/anonymization/service/ConfigurationService.java index c0582d5..c0cadfc 100644 --- a/src/main/java/com/example/anonymization/service/ConfigurationService.java +++ b/src/main/java/com/example/anonymization/service/ConfigurationService.java @@ -2,6 +2,7 @@ import com.example.anonymization.entities.Configuration; import com.example.anonymization.data.QueryService; +import com.example.anonymization.entities.ObjectGeneralizationConfig; import com.example.anonymization.exceptions.OntologyException; import jakarta.validation.constraints.NotNull; import org.apache.jena.rdf.model.*; @@ -20,6 +21,7 @@ import java.net.http.HttpResponse; import java.util.HashMap; import java.util.Map; +import java.util.Set; @Service public class ConfigurationService { @@ -80,13 +82,26 @@ private static String fetchStringContent(String url) { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder().uri(new URI(url)).GET().build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); - if (response.statusCode() < 300 || response.body().isEmpty()) { - return response.body(); + + int status = response.statusCode(); + if (status >= 200 && status < 300) { + String body = response.body(); + if (body == null || body.isEmpty()) { + throw new OntologyException( + "Successful request but empty response body for fetching ontology from: " + url + ); + } + return body; } else { - throw new OntologyException("Exception when fetching the URL content from the provided URL: " + url); + throw new OntologyException( + String.format("Failed to fetch ontology from URL: %s. HTTP status: %d", url, status) + ); } - } catch (URISyntaxException | IOException | InterruptedException e) { - throw new IllegalArgumentException("Exception when fetching the URL content from the provided URL: " + url); + } catch (URISyntaxException | IOException e) { + throw new OntologyException("Failed to fetch ontology from URL: " + url); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); // Restore thread interruption status + throw new OntologyException("Thread was interrupted while fetching ontology from URL: " + url); } } @@ -94,29 +109,40 @@ private static String fetchStringContent(String url) { private static Map> extractConfig(Model model) { Map> configs = new HashMap<>(); logger.info("Extracting configuration from server response"); - try { - QueryService.getConfigurations(model).forEach(entry -> { - if (!configs.containsKey(entry.object())) { - configs.put(entry.object(), new HashMap<>()); - } - configs.get(entry.object()).put( - entry.property(), - new Configuration( - extractValueFromURL(entry.datatype().toString()), - extractValueFromURL(entry.anonymization().toString()) - ) - ); - logger.info( - "New Config: {}, {}, {}", - extractValueFromURL(entry.property().toString()), - extractValueFromURL(entry.datatype().toString()), - extractValueFromURL(entry.anonymization().toString()) - ); - }); - logger.info("Configuration successfully converted"); - return configs; - } catch (Exception e) { - throw new OntologyException("Exception when extracting configuration from the fetched ontology"); + QueryService.getConfigurations(model).forEach(entry -> { + if (!configs.containsKey(entry.object())) { + configs.put(entry.object(), new HashMap<>()); + } + configs.get(entry.object()).put( + entry.property(), + createConfiguration(entry.datatype(), entry.anonymization(), entry.property(), model) + ); + logger.info( + "New Config: {}, {}, {}", + extractValueFromURL(entry.property().toString()), + extractValueFromURL(entry.datatype().toString()), + extractValueFromURL(entry.anonymization().toString()) + ); + }); + logger.info("Configuration successfully converted"); + return configs; + } + + private static Configuration createConfiguration( + Resource datatype, + Literal anonymization, + Property property, + Model model + ) { + String datatypeString = extractValueFromURL(datatype.toString()); + String anonymizationString = extractValueFromURL(anonymization.toString()); + if (anonymizationString.equals("generalization") && !Set.of("integer", "double", "date").contains(datatypeString)) { + return new ObjectGeneralizationConfig( + datatypeString, + QueryService.getAttributeOrder(model, property) + ); + } else { + return new Configuration(datatypeString, anonymizationString); } } diff --git a/src/main/java/com/example/anonymization/service/FaltJsonService.java b/src/main/java/com/example/anonymization/service/FaltJsonService.java index dbc4063..c94f5c2 100644 --- a/src/main/java/com/example/anonymization/service/FaltJsonService.java +++ b/src/main/java/com/example/anonymization/service/FaltJsonService.java @@ -21,6 +21,8 @@ import java.util.Set; import java.util.stream.Collectors; +import static java.util.stream.Collectors.toMap; + @Service public class FaltJsonService { @@ -36,34 +38,30 @@ public static void addDataToFlatModel(Model model, List> dat int counter = 0; Resource flatObject = model.createResource(prefix + FLAT_OBJECT_NAME); for (Map entry : data) { - try { - Resource object = model.createResource(prefix + "object" + counter); - object.addProperty(RDF.type, flatObject); - - // Add counter property - Property counterProperty = model.createProperty(prefix, "counter"); - object.addLiteral(counterProperty, counter); - - for (Map.Entry kv : entry.entrySet()) { - String key = kv.getKey(); - validateKey(key); - Object value = kv.getValue(); - if (value != null && key.equals("type")) { - if (value instanceof List) { - for (Object v : (List) value) { - object.addProperty(RDF.type, model.createResource(prefix + v.toString())); - } - continue; - } else { - object.addProperty(RDF.type, model.createResource(prefix + value)); + Resource object = model.createResource(prefix + "object" + counter); + object.addProperty(RDF.type, flatObject); + + // Add counter property + Property counterProperty = model.createProperty(prefix, "counter"); + object.addLiteral(counterProperty, counter); + + for (Map.Entry kv : entry.entrySet()) { + String key = kv.getKey(); + validateKey(key); + Object value = kv.getValue(); + if (value != null && key.equals("type")) { + if (value instanceof List) { + for (Object v : (List) value) { + object.addProperty(RDF.type, model.createResource(prefix + v.toString())); } - } - if (value != null && !key.equals("type")) { - object.addProperty(model.createProperty(prefix, key), value.toString()); + continue; + } else { + object.addProperty(RDF.type, model.createResource(prefix + value)); } } - } catch (Exception ex) { - throw new RequestModelException("Error adding data to flat model: " + ex.getMessage()); + if (value != null && !key.equals("type")) { + object.addProperty(model.createProperty(prefix, key), value.toString()); + } } counter++; } @@ -80,26 +78,40 @@ public static String createFlatJsonOutput( Map configs, Collection objectTypes, String prefix - ) { + ) throws JsonProcessingException { Resource flatObject = model.createResource(prefix + FLAT_OBJECT_NAME); - try { - Map> data = QueryService.getAllData(model, flatObject); - Map> types = QueryService.getTypesForResources(model, flatObject); - - Set classificationProperties = configs.entrySet().stream() - .filter(e -> "generalization".equals(e.getValue().getAnonymization())) - .map(Map.Entry::getKey) - .map(p -> model.getProperty(p.getURI() + "_generalized")) - .collect(Collectors.toSet()); - Map> generalizationData = - QueryService.getGeneralizationData(model, flatObject, classificationProperties); - Map kAnonymity = QueryService.getKAnonymity(model, objectTypes); - Map> attributeInformation = - QueryService.getAttributeInformation(model, objectTypes); - return createFlatJsonString(data, types, generalizationData , kAnonymity, attributeInformation); - } catch (Exception e) { - throw new AnonymizationException("Error creating flat model: " + e.getMessage()); - } + Map> data = getLiteralData(model, flatObject); + Map> types = QueryService.getTypesForResources(model, flatObject); + + Set classificationProperties = configs.entrySet().stream() + .filter(e -> "generalization".equals(e.getValue().getAnonymization())) + .map(Map.Entry::getKey) + .map(p -> model.getProperty(p.getURI() + "_generalized")) + .collect(Collectors.toSet()); + Map> generalizationData = + QueryService.getGeneralizationData(model, flatObject, classificationProperties); + Map kAnonymity = QueryService.getKAnonymity(model, objectTypes); + Map> attributeInformation = + QueryService.getAttributeInformation(model, objectTypes); + return createFlatJsonString(data, types, generalizationData , kAnonymity, attributeInformation); + } + + private static Map> getLiteralData( + Model model, + Resource flatObject + ) { + return QueryService.getAllData(model, flatObject).entrySet().stream() + .map(e -> Map.entry( + e.getKey(), + e.getValue().entrySet().stream() + .filter(inner -> inner.getValue().isLiteral()) + .collect(toMap( + Map.Entry::getKey, + inner -> inner.getValue().asLiteral() + )) + )) + .filter(e -> !e.getValue().isEmpty()) + .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); } private static String createFlatJsonString( @@ -108,19 +120,15 @@ private static String createFlatJsonString( Map> generalizationData, Map kAnonymity, Map> attributeInformation - ) { - try { - ObjectMapper mapper = new ObjectMapper(); - ArrayNode dataArray = addDataToArrayNode(mapper, data, types, generalizationData); - ObjectNode kpiNode = addKpisToObjectNode(mapper, kAnonymity, attributeInformation); - - ObjectNode root = mapper.createObjectNode(); - root.set("data", dataArray); - root.set("kpis", kpiNode); - return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(root); - } catch (JsonProcessingException e) { - throw new AnonymizationException("Error creating flat JSON output: " + e.getMessage()); - } + ) throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + ArrayNode dataArray = addDataToArrayNode(mapper, data, types, generalizationData); + ObjectNode kpiNode = addKpisToObjectNode(mapper, kAnonymity, attributeInformation); + + ObjectNode root = mapper.createObjectNode(); + root.set("data", dataArray); + root.set("kpis", kpiNode); + return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(root); } private static ArrayNode addDataToArrayNode( diff --git a/src/main/java/com/example/anonymization/service/KpiService.java b/src/main/java/com/example/anonymization/service/KpiService.java index 75d8529..0453229 100644 --- a/src/main/java/com/example/anonymization/service/KpiService.java +++ b/src/main/java/com/example/anonymization/service/KpiService.java @@ -18,7 +18,7 @@ public class KpiService { public static final String K_ANONYMITY = QueryService.SOYA_URL + "kanonymity"; public static final String HAS_ATTRIBUTE_URI = QueryService.SOYA_URL + "hasAttribute"; public static final String ANONYMIZATION_TYP_URI = QueryService.SOYA_URL + "anonymizationTyp"; - public static final String NR_ATTRIBUTES_URI = QueryService.SOYA_URL + "nrAttributes"; + public static final String NR_BUCKETS_URI = QueryService.SOYA_URL + "nrBucketsUsed"; /** * Adds a KPI object to the model containing the k-anonymity value for the given anonymization object. @@ -60,7 +60,7 @@ public static void addAttributeInformation( model.add(kpiObject, model.createProperty(HAS_ATTRIBUTE_URI), property); model.add(property, model.createProperty(ANONYMIZATION_TYP_URI), anonymizationType); if (!anonymizationType.equals("masking")) { - model.addLiteral(property, model.createProperty(NR_ATTRIBUTES_URI), nrBucketsUsed); + model.addLiteral(property, model.createProperty(NR_BUCKETS_URI), nrBucketsUsed); } } @@ -69,29 +69,25 @@ private static int calculateKAnonymity( Resource anonymizationObject, Set attributes, Map configurations ) { - try { - Map> similarValues = new HashMap<>(); - List> groups = QueryService.getGeneralizationGroups(model, anonymizationObject, attributes); - groups.forEach(group -> group.forEach( - resource -> similarValues.put(resource, new HashSet<>(group)) - )); - - attributes.stream().filter(attr -> configurations.get(attr).getAnonymization().equals("randomization")) - .forEach(randomization -> { - Map> similarity = getSimilarValues( - model, - anonymizationObject, - randomization, - configurations.get(randomization).getDataType().equals("date") - ); - similarValues.keySet().forEach( - resource -> similarValues.get(resource).retainAll(similarity.get(resource)) - ); - }); - return similarValues.values().stream().mapToInt(Set::size).min().orElse(0); - } catch (Exception ex) { - throw new AnonymizationException("Error calculating k-anonymity: " + ex.getMessage()); - } + Map> similarValues = new HashMap<>(); + List> groups = QueryService.getGeneralizationGroups(model, anonymizationObject, attributes); + groups.forEach(group -> group.forEach( + resource -> similarValues.put(resource, new HashSet<>(group)) + )); + + attributes.stream().filter(attr -> configurations.get(attr).getAnonymization().equals("randomization")) + .forEach(randomization -> { + Map> similarity = getSimilarValues( + model, + anonymizationObject, + randomization, + configurations.get(randomization).getDataType().equals("date") + ); + similarValues.keySet().forEach( + resource -> similarValues.get(resource).retainAll(similarity.get(resource)) + ); + }); + return similarValues.values().stream().mapToInt(Set::size).min().orElse(0); } private static Map> getSimilarValues( diff --git a/src/main/java/com/example/anonymization/service/anonymizer/Anonymization.java b/src/main/java/com/example/anonymization/service/anonymizer/Anonymization.java index c171417..bb59166 100644 --- a/src/main/java/com/example/anonymization/service/anonymizer/Anonymization.java +++ b/src/main/java/com/example/anonymization/service/anonymizer/Anonymization.java @@ -2,34 +2,40 @@ import com.example.anonymization.entities.Configuration; import com.example.anonymization.service.KpiService; -import jakarta.annotation.Nullable; import jakarta.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import org.apache.jena.rdf.model.Literal; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.Property; -import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.*; import java.util.Map; import static java.lang.StrictMath.floor; import static java.lang.StrictMath.pow; -@AllArgsConstructor -public abstract class Anonymization { +public abstract class Anonymization { @NotNull Model model; @NotNull Property property; - @NotNull Map data; - @NotNull Configuration config; + @NotNull Map data; + @NotNull T config; @NotNull Resource anonymizationObject; - long numberAttributes; + int numberBuckets; Anonymization( Model model, Property property, - Map data, - Configuration config, + Map data, + T config, + Resource anonymizationObject, + long numberAttributes + ) { + this(model, property, data, config, anonymizationObject); + this.numberBuckets = calculateNumberOfBuckets(data.size(), numberAttributes); + } + + Anonymization( + Model model, + Property property, + Map data, + T config, Resource anonymizationObject ) { this.model = model; @@ -47,40 +53,13 @@ public void anonymization() { KpiService.addAttributeInformation( model, property, - calculateNumberOfBuckets(data.size(), numberAttributes), + numberBuckets, config.getAnonymization(), anonymizationObject ); applyAnonymization(); } - public static Anonymization anonymizationFactoryFunction( - Configuration config, - Model model, - Property property, - Map data, - int nrAttr, - Resource anonymizationObject - ) { - return switch (config.getAnonymization()) { - case "generalization" -> switch (config.getDataType()) { - case "integer", "double" -> new GeneralizationNumeric(model, property, data, nrAttr, config, anonymizationObject); - case "date" -> new GeneralizationDate(model, property, data, nrAttr, config, anonymizationObject); - case "string" -> throw new IllegalArgumentException("No Generalization possible for type string"); - default -> new GeneralizationObject(model, property, data, nrAttr, config, anonymizationObject); - }; - case "randomization" -> switch (config.getDataType()) { - case "integer", "double" -> new RandomizationNumeric(model, property, data, nrAttr, config, anonymizationObject); - case "date" -> new RandomizationDate(model, property, data, nrAttr, config, anonymizationObject); - default -> - throw new IllegalArgumentException("No Randomization possible for type " + config.getDataType()); - }; - case "masking" -> new Masking(model, property, data, config, anonymizationObject); - default -> - throw new IllegalArgumentException("No Anonymization implementation for " + config.getAnonymization() + ": " + config.getDataType()); - }; - } - static int calculateNumberOfBuckets(long dataSize, long numberAttributes) { return (int) floor( 1.0 / pow( diff --git a/src/main/java/com/example/anonymization/service/anonymizer/Generalization.java b/src/main/java/com/example/anonymization/service/anonymizer/Generalization.java index d4426dc..420a6d5 100644 --- a/src/main/java/com/example/anonymization/service/anonymizer/Generalization.java +++ b/src/main/java/com/example/anonymization/service/anonymizer/Generalization.java @@ -4,10 +4,7 @@ import com.example.anonymization.entities.Configuration; import com.example.anonymization.data.QueryService; import org.apache.jena.atlas.lib.Pair; -import org.apache.jena.rdf.model.Literal; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.Property; -import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.*; import org.apache.jena.vocabulary.RDF; import org.apache.jena.vocabulary.RDFS; @@ -15,7 +12,7 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -public abstract class Generalization extends Anonymization { +public abstract class Generalization extends Anonymization { public static final String RDF_MAX = "http://www.w3.org/2000/01/rdf-schema#max"; public static final String RDF_MIN = "http://www.w3.org/2000/01/rdf-schema#min"; @@ -23,7 +20,7 @@ public abstract class Generalization extends Anonymization { public Generalization( Model model, Property property, - Map data, + Map data, Configuration config, Resource anonymizationObject, long numberAttributes @@ -33,16 +30,19 @@ public Generalization( @Override public void applyAnonymization() { - int nrBuckets = Anonymization.calculateNumberOfBuckets(data.size(), numberAttributes); List> sortedValues = getSortedValues(data); - List buckets = createBuckets(model, nrBuckets, sortedValues, property); - Map ranges = getRanges(sortedValues, nrBuckets, buckets); + List buckets = createBuckets(model, numberBuckets, sortedValues, property); + Map ranges = getRanges(sortedValues, numberBuckets, buckets); writeToModel(model, ranges, property); } - protected abstract List> getSortedValues(Map data); + protected abstract List> getSortedValues(Map data); - protected Map getRanges(List> sortedValues, int numberBuckets, List buckets) { + protected Map getRanges( + List> sortedValues, + int numberBuckets, + List buckets + ) { List> positionValues = new LinkedList<>(); for (int i = 0; i < sortedValues.size(); i++) { positionValues.add(new Pair<>( @@ -63,7 +63,12 @@ protected void writeToModel(Model model, Map data, Property data.forEach((key, value) -> key.addProperty(generalized, value)); } - protected List createBuckets(Model model, int nrOfBuckets, List> sortedValues, Property property) { + protected List createBuckets( + Model model, + int nrOfBuckets, + List> sortedValues, + Property property + ) { Property min = model.createProperty(RDF_MIN); Property max = model.createProperty(RDF_MAX); return IntStream.range(0, nrOfBuckets) @@ -74,12 +79,18 @@ protected List createBuckets(Model model, int nrOfBuckets, List { - public GeneralizationDate(Model model, Property property, Map data, long numberAttributes, Configuration config, Resource anonymizationObject) { + public GeneralizationDate(Model model, Property property, Map data, long numberAttributes, Configuration config, Resource anonymizationObject) { super(model, property, data, config, anonymizationObject, numberAttributes); } @Override - protected List> getSortedValues(Map data) { + protected List> getSortedValues(Map data) { return data.entrySet().stream() .map(e -> new Pair<>(e.getKey(), toDate(e.getValue()))) .sorted(Comparator.comparing(Pair::getRight)) .toList(); } - private static Calendar toDate(Literal literal) { + private static Calendar toDate(RDFNode node) { try { - XSDDateTime xsdDateTime = (XSDDateTime) XSDDatatype.XSDdate.parse(literal.getString()); + XSDDateTime xsdDateTime = (XSDDateTime) XSDDatatype.XSDdate.parse(node.asLiteral().getString()); return xsdDateTime.asCalendar(); } catch (Exception e) { - throw new IllegalArgumentException("Literal is not a valid xsd:date or xsd:dateTime: " + literal); + throw new IllegalArgumentException("Node is not Literal or not a valid xsd:date or xsd:dateTime: " + node, e); } } } diff --git a/src/main/java/com/example/anonymization/service/anonymizer/GeneralizationNumeric.java b/src/main/java/com/example/anonymization/service/anonymizer/GeneralizationNumeric.java index 4eb665b..74c6ac6 100644 --- a/src/main/java/com/example/anonymization/service/anonymizer/GeneralizationNumeric.java +++ b/src/main/java/com/example/anonymization/service/anonymizer/GeneralizationNumeric.java @@ -2,10 +2,7 @@ import com.example.anonymization.entities.Configuration; import org.apache.jena.atlas.lib.Pair; -import org.apache.jena.rdf.model.Literal; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.Property; -import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.*; import java.util.List; import java.util.Map; @@ -15,7 +12,7 @@ public class GeneralizationNumeric extends Generalization { public GeneralizationNumeric( Model model, Property property, - Map data, + Map data, long numberAttributes, Configuration config, Resource anonymizationObject @@ -24,10 +21,14 @@ public GeneralizationNumeric( } @Override - protected List> getSortedValues(Map data) { - return data.entrySet().stream() - .map(e -> new Pair<>(e.getKey(), e.getValue().getDouble())) - .sorted((e1, e2) -> (int) (e1.getRight() - e2.getRight())) - .toList(); + protected List> getSortedValues(Map data) { + try { + return data.entrySet().stream() + .map(e -> new Pair<>(e.getKey(), e.getValue().asLiteral().getDouble())) + .sorted((e1, e2) -> (int) (e1.getRight() - e2.getRight())) + .toList(); + } catch (Exception e) { + throw new IllegalArgumentException("Error while parsing numeric values for generalization.", e); + } } } diff --git a/src/main/java/com/example/anonymization/service/anonymizer/GeneralizationObject.java b/src/main/java/com/example/anonymization/service/anonymizer/GeneralizationObject.java index 0d56271..d64303a 100644 --- a/src/main/java/com/example/anonymization/service/anonymizer/GeneralizationObject.java +++ b/src/main/java/com/example/anonymization/service/anonymizer/GeneralizationObject.java @@ -1,28 +1,60 @@ package com.example.anonymization.service.anonymizer; import com.example.anonymization.entities.Configuration; -import org.apache.jena.rdf.model.Literal; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.Property; -import org.apache.jena.rdf.model.Resource; +import com.example.anonymization.entities.ObjectGeneralizationConfig; +import org.apache.jena.rdf.model.*; +import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; -public class GeneralizationObject extends Anonymization { +public class GeneralizationObject extends Anonymization { public GeneralizationObject( Model model, Property property, - Map data, + Map data, long numberAttributes, Configuration config, Resource anonymizationObject ) { - super(model, property, data, config, anonymizationObject, numberAttributes); + super(model, property, data, (ObjectGeneralizationConfig) config, anonymizationObject, numberAttributes); } @Override public void applyAnonymization() { - // TODO + Property generalized = model.createProperty(property.getURI(), "_generalized"); + List attributes = config.getAttributeOrder().stream() + .map(p -> model.getProperty("https://soya.ownyourdata.eu/AnonymisationDemo/" + p)) + .toList(); + for (Property objectProperty : attributes) { + Map propertyData = data.entrySet() + .stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + e -> { + Statement stmt = model.getProperty(e.getValue().asResource(), objectProperty); + return stmt != null && stmt.getObject().isLiteral() ? + stmt.getLiteral() : model.createLiteral(""); + } + )); + if (checkIfAnonymizationIsEnough(propertyData)) { + propertyData.forEach((key, value) -> key.addProperty(generalized, value)); + return; + } + } + data.keySet().forEach(key -> key.addProperty(generalized, model.createLiteral("*****"))); + } + + private boolean checkIfAnonymizationIsEnough(Map propertyData) { + Collection groupCounts = propertyData.entrySet().stream() + .collect(Collectors.groupingBy( + e -> e.getValue().getValue(), + Collectors.counting() + )) + .values(); + return groupCounts.size() <= numberBuckets && Collections.min(groupCounts) > data.size() * 0.5 / numberBuckets; } } diff --git a/src/main/java/com/example/anonymization/service/anonymizer/Masking.java b/src/main/java/com/example/anonymization/service/anonymizer/Masking.java index dfc833a..fa716a7 100644 --- a/src/main/java/com/example/anonymization/service/anonymizer/Masking.java +++ b/src/main/java/com/example/anonymization/service/anonymizer/Masking.java @@ -1,20 +1,17 @@ package com.example.anonymization.service.anonymizer; import com.example.anonymization.entities.Configuration; -import org.apache.jena.rdf.model.Literal; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.Property; -import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.*; import java.util.Map; -public class Masking extends Anonymization { +public class Masking extends Anonymization { public Masking( Model model, Property property, - Map data, + Map data, Configuration config, Resource anonymizationObject ) { diff --git a/src/main/java/com/example/anonymization/service/anonymizer/Randomization.java b/src/main/java/com/example/anonymization/service/anonymizer/Randomization.java index 6816bb2..4e55fce 100644 --- a/src/main/java/com/example/anonymization/service/anonymizer/Randomization.java +++ b/src/main/java/com/example/anonymization/service/anonymizer/Randomization.java @@ -1,20 +1,16 @@ package com.example.anonymization.service.anonymizer; import com.example.anonymization.entities.Configuration; -import com.example.anonymization.service.KpiService; -import org.apache.jena.rdf.model.Literal; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.Property; -import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.*; import java.util.*; -public abstract class Randomization extends Anonymization { +public abstract class Randomization extends Anonymization { public Randomization( Model model, Property property, - Map data, + Map data, Configuration config, Resource anonymizationObject, long numberAttributes @@ -30,16 +26,13 @@ public Randomization( @Override public void applyAnonymization() { - int nrBuckets = Anonymization.calculateNumberOfBuckets(data.size(), numberAttributes); - int randomizationValue = data.size() / nrBuckets; + int randomizationValue = data.size() / numberBuckets; Map randomizedValues = getRandomizedValues(data, randomizationValue); writeToModel(model, randomizedValues, property); } - private Map getRandomizedValues(Map data, int randomizationValue) { - List> sorted = data.entrySet().stream() - .sorted((e1, e2) -> getComparator().compare(e1.getValue(), e2.getValue())) - .toList(); + private Map getRandomizedValues(Map data, int randomizationValue) { + List> sorted = getSortedLiteralEntries(data); Map randomized = HashMap.newHashMap(data.size()); randomizationValue = randomizationValue == data.size() ? randomizationValue - 1 : randomizationValue; int lowerBound = 0; @@ -50,7 +43,10 @@ private Map getRandomizedValues(Map data, lowerBound < sorted.size() - (randomizationValue + 1) && (lowerBound < idx - randomizationValue || Math.abs(distance(sorted.get(lowerBound).getValue(), entry.getValue())) > - Math.abs(distance(sorted.get(lowerBound + randomizationValue + 1).getValue(), entry.getValue())) + Math.abs(distance( + sorted.get(lowerBound + randomizationValue + 1).getValue(), + entry.getValue() + )) ) ) { lowerBound++; @@ -70,6 +66,18 @@ private Map getRandomizedValues(Map data, return randomized; } + private List> getSortedLiteralEntries(Map data) { + try { + return data.entrySet().stream() + .map(entry -> Map.entry(entry.getKey(), entry.getValue().asLiteral())) + .sorted((e1, e2) -> + getComparator().compare(e1.getValue(), e2.getValue())) + .toList(); + } catch (Exception e) { + throw new IllegalArgumentException("Randomization can only be applied to literal values."); + } + } + private void writeToModel(Model model, Map randomizedValues, Property originalProperty) { Property randomized = model.createProperty(originalProperty.getURI(), "_randomized"); randomizedValues.forEach((key, value) -> key.addLiteral(randomized, value)); diff --git a/src/main/java/com/example/anonymization/service/anonymizer/RandomizationDate.java b/src/main/java/com/example/anonymization/service/anonymizer/RandomizationDate.java index c8ffce9..a2123d3 100644 --- a/src/main/java/com/example/anonymization/service/anonymizer/RandomizationDate.java +++ b/src/main/java/com/example/anonymization/service/anonymizer/RandomizationDate.java @@ -1,7 +1,6 @@ package com.example.anonymization.service.anonymizer; import com.example.anonymization.entities.Configuration; -import org.apache.jena.base.Sys; import org.apache.jena.datatypes.xsd.XSDDatatype; import org.apache.jena.datatypes.xsd.XSDDateTime; import org.apache.jena.rdf.model.*; @@ -16,7 +15,7 @@ public class RandomizationDate extends Randomization { public RandomizationDate( Model model, Property property, - Map data, + Map data, long numberAttributes, Configuration config, Resource anonymizationObject diff --git a/src/main/java/com/example/anonymization/service/anonymizer/RandomizationNumeric.java b/src/main/java/com/example/anonymization/service/anonymizer/RandomizationNumeric.java index 3c3aae6..54a4bde 100644 --- a/src/main/java/com/example/anonymization/service/anonymizer/RandomizationNumeric.java +++ b/src/main/java/com/example/anonymization/service/anonymizer/RandomizationNumeric.java @@ -10,7 +10,7 @@ public class RandomizationNumeric extends Randomization { public RandomizationNumeric( Model model, Property property, - Map data, + Map data, long numberAttributes, Configuration config, Resource anonymizationObject diff --git a/src/main/resources/static/examples/anonymization-request-object.json b/src/main/resources/static/examples/anonymization-request-object.json new file mode 100644 index 0000000..f50553b --- /dev/null +++ b/src/main/resources/static/examples/anonymization-request-object.json @@ -0,0 +1,90 @@ +{ + "configurationUrl": "https://soya.ownyourdata.eu/AnonymisationDemo", + "data": { + "@context": { + "oyd": "https://soya.ownyourdata.eu/AnonymisationDemo/" + }, + "@graph": [ + { + "@id": "oyd:test1", + "@type": "oyd:AnonymisationDemo", + "oyd:adresse": { + "@id": "oyd:address1" + } + }, + { + "@id": "oyd:address1", + "@type": "oyd:Address", + "oyd:detail": "Musterstrasse 1", + "oyd:city": "Vienna", + "oyd:zip": "1010", + "oyd:state": "Vienna", + "oyd:country": "Austria" + }, + { + "@id": "oyd:test2", + "@type": "oyd:AnonymisationDemo", + "oyd:adresse": { + "@id": "oyd:address2" + } + }, + { + "@id": "oyd:address2", + "@type": "oyd:Address", + "oyd:detail": "Anderestrasse 1", + "oyd:city": "Vienna", + "oyd:zip": "1010", + "oyd:state": "Vienna", + "oyd:country": "Austria" + }, + { + "@id": "oyd:test3", + "@type": "oyd:AnonymisationDemo", + "oyd:adresse": { + "@id": "oyd:address3" + } + }, + { + "@id": "oyd:address3", + "@type": "oyd:Address", + "oyd:detail": "Vogerlweg 5", + "oyd:city": "Krems", + "oyd:zip": "2020", + "oyd:state": "Krems", + "oyd:country": "Austria" + }, + { + "@id": "oyd:test4", + "@type": "oyd:AnonymisationDemo", + "oyd:adresse": { + "@id": "oyd:address4" + } + }, + { + "@id": "oyd:address4", + "@type": "oyd:Address", + "oyd:detail": "Hauptstrasse 10", + "oyd:city": "Salzburg", + "oyd:zip": "5020", + "oyd:state": "Salzburg", + "oyd:country": "Austria" + }, + { + "@id": "oyd:test5", + "@type": "oyd:AnonymisationDemo", + "oyd:adresse": { + "@id": "oyd:address5" + } + }, + { + "@id": "oyd:address5", + "@type": "oyd:Address", + "oyd:detail": "Bahnhofstrasse 3", + "oyd:city": "Innsbruck", + "oyd:zip": "6020", + "oyd:state": "Innsbruck", + "oyd:country": "Austria" + } + ] + } +} \ No newline at end of file