diff --git a/README.md b/README.md index 75fe6f55..f8d3a3c9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # Modifiable Variables ![licence](https://img.shields.io/badge/License-Apachev2-brightgreen.svg) -[![jenkins](https://hydrogen.cloud.nds.rub.de/buildStatus/icon.svg?job=ModifiableVariable)](https://hydrogen.cloud.nds.rub.de/job/ModifiableVariable/) Modifiable Variable allows you to set modifications to basic types after or before their values are actually determined. When their actual values are determined and you access the value via getters, the original value will be returned in a modified form according to the defined modifications. @@ -94,289 +93,319 @@ VariableModification bigIntMod = new BigIntegerAddModification(BigIn VariableModification byteArrayMod = new ByteArrayXorModification(new byte[] {2, 3}, 0); ``` -# Modifiable variables in XML +# Modifiable Variables in JSON -Modifiable variables are serializable with Jakarta XML Binding (JAXB) into XML. You can use the following code to do that: +Modifiable variables are serializable into JSON using Jackson. You can use the following code to do that: ```java ModifiableByteArray mba = new ModifiableByteArray(); mba.setOriginalValue(new byte[]{1, 2, 3}); -StringWriter writer = new StringWriter(); - -// Create a JAXB context with all classes you'll need for serialization -JAXBContext context = JAXBContext.newInstance( - ModifiableByteArray.class, - ByteArrayDeleteModification.class, - ByteArrayExplicitValueModification.class, - ByteArrayInsertValueModification.class, - ByteArrayXorModification.class -); - -// Create a marshaller with formatted output -Marshaller m = context.createMarshaller(); -m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + +// Create a Jackson object mapper for serialization +ObjectMapper mapper = new ObjectMapper(); +mapper.registerModule(new ModifiableVariableModule()); +mapper.setVisibility(ModifiableVariableModule.getFieldVisibilityChecker()); -// Marshal the array into XML -m.marshal(mba, writer); -String xmlString = writer.toString(); -System.out.println(xmlString); +// Serialize the array into JSON +String jsonString = mapper.writeValueAsString(mba); +System.out.println(jsonString); -// Use the XML to create a modifiable byte array variable again -Unmarshaller um = context.createUnmarshaller(); -ModifiableByteArray test = (ModifiableByteArray) um.unmarshal(new StringReader(xmlString)); -System.out.println(ArrayConverter.bytesToHexString(test.getValue())); +// Use the JSON to create a modifiable byte array variable again +ModifiableByteArray test = mapper.readValue(jsonString, ModifiableByteArray.class); +System.out.println(DataConverter.bytesToHexString(test.getValue())); ``` -When creating the JAXBContext, make sure to include all modification classes you intend to use. The example above only includes a subset of the available modifications. +When creating the object mapper, make sure to register an instance of the `ModifiableVariableModule` and set the +visibility to `ModifiableVariableModule.getFieldVisibilityChecker()` to ensure proper (de-)serialization. +When using custom modification or variable classes, you will need to register the corresponding classes with the object +mappers' `registerSubtypes` method. All classes that are part of the ModifiableVariable package are already registered in the `ModifiableVariableModule`. The result of the serialized modifiable byte array looks as follows: -```xml - - 01 02 03 - +```json +{ + "@type": "ModifiableByteArray", + "originalValue": "010203" +} ``` If you were to use a byte array with an insertion modification, the result would look as follows: -```xml - - 01 02 03 - - - 02 03 - 1 - - - +```json +{ + "@type": "ModifiableByteArray", + "modifications": [ + { + "@type": "ByteArrayInsertValueModification", + "bytesToInsert": "0203", + "startPosition": 1 + } + ], + "originalValue":"010203" +} ``` -Below are examples of XML representations for various modification types: +A schema for the JSON representation of a single `ModifiableVariable` including all standard variable and modification types +can be found in the `src/main/resources` directory. The schema is named `ModifiableVariable.schema.json`. + +Below are examples of JSON representations for various modification types. ## Integer Modifications - Explicit value: -```xml - - 25872 - +```json +{ + "@type": "IntegerExplicitValueModification", + "explicitValue": 25872 +} ``` - Add: -```xml - - 960 - +```json +{ + "@type": "IntegerAddModification", + "summand": 960 +} ``` - Subtract: -```xml - - 30959 - +```json +{ + "@type": "IntegerSubtractModification", + "subtrahend": 30959 +} ``` - Multiply: -```xml - - 2 - +```json +{ + "@type": "IntegerMultiplyModification", + "factor": 2 +} ``` - Right shift: -```xml - - 13 - +```json +{ + "@type": "IntegerShiftRightModification", + "shift": 13 +} ``` - Left shift: -```xml - - 13 - +```json +{ + "@type": "IntegerShiftLeftModification", + "shift": 13 +} ``` - XOR: -```xml - - 22061 - +```json +{ + "@type": "IntegerXorModification", + "xor": 22061 +} ``` - Swap endian: -```xml - +```json +{ + "@type": "IntegerSwapEndianModification" +} ``` ## BigInteger Modifications -BigInteger supports similar operations to Integer: +BigInteger supports similar operations to Integer, for example: -```xml - - 1 - +```json +{ + "@type": "BigIntegerAddModification", + "summand": 1 +} ``` -```xml - - 42 - +```json +{ + "@type": "BigIntegerMultiplyModification", + "factor": 42 +} ``` -```xml - - 1337 - +```json +{ + "@type": "BigIntegerXorModification", + "xor": 1337 +} ``` ## Long Modifications -ModifiableLong supports the same operations as Integer: +ModifiableLong supports the same operations as Integer, for example: -```xml - - 10000 - +```json +{ + "@type": "LongAddModification", + "summand": 10000 +} ``` -```xml - +```json +{ + "@type": "LongSwapEndianModification" +} ``` ## Byte Array Modifications - Explicit value: -```xml - - 4F 3F 8C FC 17 8E 66 0A 53 DF 4D 4E E9 0B D0 - +```json +{ + "@type": "ByteArrayExplicitValueModification", + "explicitValue": "4F3F8CFC178E660A53DF4D4EE90BD0" +} ``` - XOR: -```xml - - 11 22 - 1 - +```json +{ + "@type": "ByteArrayXorModification", + "xor": "1122", + "startPosition": 1 +} ``` - Insert: -```xml - - 3D 9F 3B 77 65 03 F9 8A 93 6D 94 CD 7E 4A C5 1B - 0 - +```json +{ + "@type": "ByteArrayInsertValueModification", + "bytesToInsert": "3D9F3B776503F98A936D94CD7E4AC51B", + "startPosition": 0 +} ``` - Append: -```xml - - AA BB CC - +```json +{ + "@type": "ByteArrayAppendValueModification", + "bytesToAppend": "AABBCC" +} ``` - Prepend: -```xml - - AA BB CC - +```json +{ + "@type": "ByteArrayPrependValueModification", + "bytesToPrepend": "AABBCC" +} ``` - Delete: -```xml - - 2 - 0 - +```json +{ + "@type": "ByteArrayDeleteModification", + "count": 2, + "startPosition": 0 +} ``` - Duplicate: -```xml - +```json +{ + "@type": "ByteArrayDuplicateModification" +} ``` - Shuffle: -```xml - - 00 01 - +```json +{ + "@type": "ByteArrayShuffleModification", + "shuffle": [ 0, 1 ] +} ``` ## Boolean Modifications - Explicit value: -```xml - - true - +```json +{ + "@type": "BooleanExplicitValueModification", + "explicitValue": true +} ``` -- Toggle: +- Toggle / Negation: -```xml - +```json +{ + "@type": "BooleanToggleModification" +} ``` ## String Modifications - Explicit value: -```xml - - abc - +```json +{ + "@type": "StringExplicitValueModification", + "explicitValue": "abc" +} ``` - Append value: -```xml - - def - +```json +{ + "@type": "StringAppendValueModification", + "appendValue": "def" +} ``` - Prepend value: -```xml - - xyz - +```json +{ + "@type": "StringPrependValueModification", + "prependValue": "xyz" +} ``` - Insert value: -```xml - - 123 - 2 - +```json +{ + "@type": "StringInsertValueModification", + "insertValue": "123", + "insertPosition": 2 +} ``` - Delete: -```xml - - 2 - 1 - +```json +{ + "@type": "StringDeleteModification", + "count": 2, + "startPosition": 1 +} ``` diff --git a/pom.xml b/pom.xml index 89c1d115..820743de 100644 --- a/pom.xml +++ b/pom.xml @@ -67,6 +67,11 @@ + + + com.fasterxml + classmate + com.fasterxml.jackson.core jackson-annotations @@ -80,10 +85,13 @@ jackson-databind - com.fasterxml.jackson.dataformat - jackson-dataformat-yaml + com.github.victools + jsonschema-generator + + + com.github.victools + jsonschema-module-jackson - jakarta.xml.bind jakarta.xml.bind-api @@ -100,17 +108,16 @@ org.apache.logging.log4j log4j-core + + org.reflections + reflections + com.github.spotbugs spotbugs-annotations provided - - org.glassfish.jaxb - jaxb-runtime - test - org.junit.jupiter junit-jupiter @@ -211,6 +218,28 @@ ${skip.surefire.tests} + + + org.codehaus.mojo + exec-maven-plugin + + + generate-json-schema + + java + + prepare-package + + de.rub.nds.modifiablevariable.json.JsonSchemaCliGenerator + + de.rub.nds.modifiablevariable.ModifiableVariable + src/main/resources/ModifiableVariable.schema.json + de.rub.nds.modifiablevariable.json.ModifiableVariableModule + + + + + org.apache.maven.plugins diff --git a/src/main/java/de/rub/nds/modifiablevariable/ModifiableVariable.java b/src/main/java/de/rub/nds/modifiablevariable/ModifiableVariable.java index dfa145da..9ac3b2cd 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/ModifiableVariable.java +++ b/src/main/java/de/rub/nds/modifiablevariable/ModifiableVariable.java @@ -7,7 +7,7 @@ */ package de.rub.nds.modifiablevariable; -import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.*; import jakarta.xml.bind.annotation.*; import java.io.Serializable; import java.util.LinkedList; @@ -32,15 +32,20 @@ */ @XmlTransient @XmlAccessorType(XmlAccessType.FIELD) -@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class") +@JsonTypeInfo( + use = JsonTypeInfo.Id.SIMPLE_NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "@type") public abstract class ModifiableVariable implements Serializable { /** The list of modifications that will be applied to the original value when accessed */ @XmlElementWrapper @XmlAnyElement(lax = true) + @JsonInclude(JsonInclude.Include.NON_EMPTY) private LinkedList> modifications; /** The expected value for assertion validation */ + @JsonInclude(JsonInclude.Include.NON_NULL) protected E assertEquals; /** Default constructor that creates an empty modifiable variable. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/VariableModification.java b/src/main/java/de/rub/nds/modifiablevariable/VariableModification.java index adc3d9da..7504f0df 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/VariableModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/VariableModification.java @@ -46,7 +46,10 @@ */ @XmlTransient @XmlAccessorType(XmlAccessType.FIELD) -@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class") +@JsonTypeInfo( + use = JsonTypeInfo.Id.SIMPLE_NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "@type") public abstract class VariableModification implements Serializable { /** Logger for debugging modification applications */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerAddModification.java b/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerAddModification.java index fdccc3ca..c0d49628 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerAddModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerAddModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.biginteger; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.math.BigInteger; @@ -23,6 +24,7 @@ public class BigIntegerAddModification extends VariableModification { /** The value to add to the original BigInteger */ + @JsonProperty(required = true) private BigInteger summand; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerExplicitValueModification.java b/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerExplicitValueModification.java index 0e0e00a3..2f9a631c 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerExplicitValueModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerExplicitValueModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.biginteger; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.math.BigInteger; @@ -25,6 +26,7 @@ public class BigIntegerExplicitValueModification extends VariableModification { /** The explicit value that will replace the original value */ + @JsonProperty(required = true) private BigInteger explicitValue; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerMultiplyModification.java b/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerMultiplyModification.java index fb823e75..03222fcb 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerMultiplyModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerMultiplyModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.biginteger; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.math.BigInteger; @@ -24,6 +25,7 @@ public class BigIntegerMultiplyModification extends VariableModification { /** The factor to multiply by */ + @JsonProperty(required = true) private BigInteger factor; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerShiftLeftModification.java b/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerShiftLeftModification.java index a25aae7b..6539df7f 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerShiftLeftModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerShiftLeftModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.biginteger; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.math.BigInteger; @@ -25,6 +26,7 @@ public class BigIntegerShiftLeftModification extends VariableModification { /** The number of bits to shift left */ + @JsonProperty(required = true) private int shift; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerShiftRightModification.java b/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerShiftRightModification.java index 3cb9bcdf..fed5ae57 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerShiftRightModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerShiftRightModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.biginteger; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.math.BigInteger; @@ -25,6 +26,7 @@ public class BigIntegerShiftRightModification extends VariableModification { /** The number of bit positions to shift right */ + @JsonProperty(required = true) private int shift; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerSubtractModification.java b/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerSubtractModification.java index 0a47b247..16aee158 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerSubtractModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerSubtractModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.biginteger; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.math.BigInteger; @@ -24,6 +25,7 @@ public class BigIntegerSubtractModification extends VariableModification { /** The value to subtract from the original BigInteger */ + @JsonProperty(required = true) private BigInteger subtrahend; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerXorModification.java b/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerXorModification.java index 5d3fce80..0d1b071f 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerXorModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/biginteger/BigIntegerXorModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.biginteger; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.math.BigInteger; @@ -27,6 +28,7 @@ public class BigIntegerXorModification extends VariableModification { /** The BigInteger value to XOR with the original value */ + @JsonProperty(required = true) private BigInteger xor; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/biginteger/ModifiableBigInteger.java b/src/main/java/de/rub/nds/modifiablevariable/biginteger/ModifiableBigInteger.java index 520ee387..e439983b 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/biginteger/ModifiableBigInteger.java +++ b/src/main/java/de/rub/nds/modifiablevariable/biginteger/ModifiableBigInteger.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.biginteger; +import com.fasterxml.jackson.annotation.JsonInclude; import de.rub.nds.modifiablevariable.ModifiableVariable; import de.rub.nds.modifiablevariable.util.DataConverter; import jakarta.xml.bind.annotation.XmlRootElement; @@ -31,6 +32,7 @@ public class ModifiableBigInteger extends ModifiableVariable { /** The original BigInteger value before any modifications */ + @JsonInclude(JsonInclude.Include.NON_NULL) private BigInteger originalValue; /** Default constructor that creates an empty ModifiableBigInteger with no original value. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/bool/BooleanExplicitValueModification.java b/src/main/java/de/rub/nds/modifiablevariable/bool/BooleanExplicitValueModification.java index 08a59733..93fdf1dd 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/bool/BooleanExplicitValueModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/bool/BooleanExplicitValueModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.bool; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; @@ -24,6 +25,7 @@ public class BooleanExplicitValueModification extends VariableModification { /** The explicit boolean value that will replace the original value */ + @JsonProperty(required = true) private boolean explicitValue; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/bool/ModifiableBoolean.java b/src/main/java/de/rub/nds/modifiablevariable/bool/ModifiableBoolean.java index 04a13110..35ffd239 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/bool/ModifiableBoolean.java +++ b/src/main/java/de/rub/nds/modifiablevariable/bool/ModifiableBoolean.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.bool; +import com.fasterxml.jackson.annotation.JsonInclude; import de.rub.nds.modifiablevariable.ModifiableVariable; import jakarta.xml.bind.annotation.XmlRootElement; @@ -24,6 +25,7 @@ public class ModifiableBoolean extends ModifiableVariable { /** The original Boolean value before any modifications */ + @JsonInclude(JsonInclude.Include.NON_NULL) private Boolean originalValue; /** Default constructor that creates an empty ModifiableBoolean with no original value. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/bytearray/ByteArrayAppendValueModification.java b/src/main/java/de/rub/nds/modifiablevariable/bytearray/ByteArrayAppendValueModification.java index 7ba7a2a4..d4e97bfa 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/bytearray/ByteArrayAppendValueModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/bytearray/ByteArrayAppendValueModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.bytearray; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import de.rub.nds.modifiablevariable.util.DataConverter; import de.rub.nds.modifiablevariable.util.UnformattedByteArrayAdapter; @@ -32,6 +33,7 @@ public class ByteArrayAppendValueModification extends VariableModification { /** The number of bytes to delete */ + @JsonProperty(required = true) private int count; /** The position from which to start deletion (0-based index) */ + @JsonProperty(required = true) private int startPosition; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/bytearray/ByteArrayExplicitValueModification.java b/src/main/java/de/rub/nds/modifiablevariable/bytearray/ByteArrayExplicitValueModification.java index 6090172d..5cbd1156 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/bytearray/ByteArrayExplicitValueModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/bytearray/ByteArrayExplicitValueModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.bytearray; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import de.rub.nds.modifiablevariable.util.DataConverter; import de.rub.nds.modifiablevariable.util.UnformattedByteArrayAdapter; @@ -29,6 +30,7 @@ public class ByteArrayExplicitValueModification extends VariableModification { /** The shuffle pattern defining which indices to swap */ + @JsonProperty(required = true) private int[] shuffle; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/bytearray/ByteArrayXorModification.java b/src/main/java/de/rub/nds/modifiablevariable/bytearray/ByteArrayXorModification.java index 1fdd9b06..f70df807 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/bytearray/ByteArrayXorModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/bytearray/ByteArrayXorModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.bytearray; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import de.rub.nds.modifiablevariable.util.DataConverter; import de.rub.nds.modifiablevariable.util.UnformattedByteArrayAdapter; @@ -29,9 +30,11 @@ public class ByteArrayXorModification extends VariableModification { /** The byte array to XOR with the original byte array */ @XmlJavaTypeAdapter(UnformattedByteArrayAdapter.class) + @JsonProperty(required = true) private byte[] xor; /** The position in the original byte array where the XOR operation starts */ + @JsonProperty(required = true) private int startPosition; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/bytearray/ModifiableByteArray.java b/src/main/java/de/rub/nds/modifiablevariable/bytearray/ModifiableByteArray.java index 5c6b017b..f78867db 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/bytearray/ModifiableByteArray.java +++ b/src/main/java/de/rub/nds/modifiablevariable/bytearray/ModifiableByteArray.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.bytearray; +import com.fasterxml.jackson.annotation.JsonInclude; import de.rub.nds.modifiablevariable.ModifiableVariable; import de.rub.nds.modifiablevariable.util.DataConverter; import de.rub.nds.modifiablevariable.util.UnformattedByteArrayAdapter; @@ -45,6 +46,7 @@ public class ModifiableByteArray extends ModifiableVariable { /** The original byte array value before any modifications */ + @JsonInclude(JsonInclude.Include.NON_NULL) private byte[] originalValue; /** Default constructor that creates an empty ModifiableByteArray with no original value. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerAddModification.java b/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerAddModification.java index 5d44bc73..f8bc4955 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerAddModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerAddModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.integer; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.util.Objects; @@ -23,6 +24,7 @@ public class IntegerAddModification extends VariableModification { /** The value to add to the original integer */ + @JsonProperty(required = true) private int summand; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerExplicitValueModification.java b/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerExplicitValueModification.java index d367f362..4d870a05 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerExplicitValueModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerExplicitValueModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.integer; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.util.Objects; @@ -23,6 +24,7 @@ public class IntegerExplicitValueModification extends VariableModification { /** The explicit value that will replace the original value */ + @JsonProperty(required = true) protected int explicitValue; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerMultiplyModification.java b/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerMultiplyModification.java index e36ce3b1..9f2dd0fe 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerMultiplyModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerMultiplyModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.integer; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.util.Objects; @@ -23,6 +24,7 @@ public class IntegerMultiplyModification extends VariableModification { /** The factor by which to multiply the original integer value */ + @JsonProperty(required = true) private int factor; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerShiftLeftModification.java b/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerShiftLeftModification.java index 66518d86..ba6573e1 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerShiftLeftModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerShiftLeftModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.integer; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; @@ -24,6 +25,7 @@ public class IntegerShiftLeftModification extends VariableModification { /** The number of bit positions to shift left */ + @JsonProperty(required = true) private int shift; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerShiftRightModification.java b/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerShiftRightModification.java index ec66eed1..57d0970d 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerShiftRightModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerShiftRightModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.integer; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; @@ -24,6 +25,7 @@ public class IntegerShiftRightModification extends VariableModification { /** The number of bit positions to shift right */ + @JsonProperty(required = true) private int shift; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerSubtractModification.java b/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerSubtractModification.java index c98c4546..34c0871f 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerSubtractModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerSubtractModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.integer; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.util.Objects; @@ -24,6 +25,7 @@ public class IntegerSubtractModification extends VariableModification { /** The value to subtract from the original integer */ + @JsonProperty(required = true) private int subtrahend; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerXorModification.java b/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerXorModification.java index 4c50ec6d..6c718dde 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerXorModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/integer/IntegerXorModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.integer; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.util.Objects; @@ -24,6 +25,7 @@ public class IntegerXorModification extends VariableModification { /** The XOR mask to apply to the original integer */ + @JsonProperty(required = true) private int xor; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/integer/ModifiableInteger.java b/src/main/java/de/rub/nds/modifiablevariable/integer/ModifiableInteger.java index 5d57e10f..cdf2cb07 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/integer/ModifiableInteger.java +++ b/src/main/java/de/rub/nds/modifiablevariable/integer/ModifiableInteger.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.integer; +import com.fasterxml.jackson.annotation.JsonInclude; import de.rub.nds.modifiablevariable.ModifiableVariable; import de.rub.nds.modifiablevariable.util.DataConverter; import jakarta.xml.bind.annotation.XmlRootElement; @@ -31,6 +32,7 @@ public class ModifiableInteger extends ModifiableVariable { /** The original integer value before any modifications */ + @JsonInclude(JsonInclude.Include.NON_NULL) private Integer originalValue; /** Default constructor that creates an empty ModifiableInteger with no original value. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/json/JsonSchemaCliGenerator.java b/src/main/java/de/rub/nds/modifiablevariable/json/JsonSchemaCliGenerator.java new file mode 100644 index 00000000..3f4ec158 --- /dev/null +++ b/src/main/java/de/rub/nds/modifiablevariable/json/JsonSchemaCliGenerator.java @@ -0,0 +1,71 @@ +/* + * ModifiableVariable - A Variable Concept for Runtime Modifications + * + * Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH + * + * Licensed under Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 + */ +package de.rub.nds.modifiablevariable.json; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.victools.jsonschema.generator.*; +import java.io.File; +import java.lang.reflect.InvocationTargetException; + +public class JsonSchemaCliGenerator { + + /** + * A command line tool to generate a JSON schema for a given class using Jackson and victools. + * + * @param args args[0] is the fully qualified class name for which to generate the schema, + * args[1] is the output file path, and any additional arguments are class names of + * additional Jackson modules to register. + */ + public static void main(String[] args) { + ObjectMapper mapper = new ObjectMapper(); + mapper.setVisibility(ModifiableVariableModule.getFieldVisibilityChecker()); + + // Register additional modules (if any) passed as command line arguments + for (int i = 2; i < args.length; i++) { + try { + mapper.registerModule( + (Module) Class.forName(args[i]).getConstructor().newInstance()); + } catch (InstantiationException + | IllegalAccessException + | InvocationTargetException + | NoSuchMethodException + | ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + // Prepare the schema generator + SchemaGeneratorConfigBuilder builder = + new SchemaGeneratorConfigBuilder( + mapper, SchemaVersion.DRAFT_2020_12, OptionPreset.PLAIN_JSON); + builder.with(new ReflectiveJacksonSchemaModule()); + SchemaGeneratorConfig config = builder.build(); + SchemaGenerator generator = new SchemaGenerator(config); + JsonNode jsonSchema; + + // Generate the schema for the specified class + try { + jsonSchema = generator.generateSchema(Class.forName(args[0])); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + + // Output the generated schema to the specified file + File outputFile = new File(args[1]); + //noinspection AssertWithSideEffects + assert outputFile.exists() || outputFile.mkdirs(); + try { + mapper.writerWithDefaultPrettyPrinter().writeValue(outputFile, jsonSchema); + } catch (Exception e) { + throw new RuntimeException( + "Failed to write JSON schema to file: " + outputFile.getAbsolutePath(), e); + } + } +} diff --git a/src/main/java/de/rub/nds/modifiablevariable/json/ModifiableVariableModule.java b/src/main/java/de/rub/nds/modifiablevariable/json/ModifiableVariableModule.java new file mode 100644 index 00000000..afc41d0c --- /dev/null +++ b/src/main/java/de/rub/nds/modifiablevariable/json/ModifiableVariableModule.java @@ -0,0 +1,78 @@ +/* + * ModifiableVariable - A Variable Concept for Runtime Modifications + * + * Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH + * + * Licensed under Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 + */ +package de.rub.nds.modifiablevariable.json; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.introspect.VisibilityChecker; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import de.rub.nds.modifiablevariable.ModifiableVariable; +import de.rub.nds.modifiablevariable.VariableModification; +import de.rub.nds.modifiablevariable.util.DataConverter; +import java.io.IOException; +import org.reflections.Reflections; + +/** + * A Jackson module for the ModifiableVariable library. It registers serializers and deserializers + * required for modifiable variables. Make sure to include it using {@code + * ObjectMapper.registerModule(new ModifiableVariableModule())} in your Jackson configuration before + * serializing or deserializing modifiable variables. + */ +public class ModifiableVariableModule extends SimpleModule { + + private static final String MODULE_NAME = "ModifiableVariableModule"; + + /** Default constructor that sets the module name and version. */ + public ModifiableVariableModule() { + super(MODULE_NAME); + // Serializers + addSerializer(byte[].class, new UnformattedByteArraySerializer()); + // Deserializers + addDeserializer(byte[].class, new UnformattedByteArrayDeserializer()); + // Subtypes + Reflections reflections = new Reflections("de.rub.nds.modifiablevariable"); + reflections.getSubTypesOf(ModifiableVariable.class).forEach(this::registerSubtypes); + reflections.getSubTypesOf(VariableModification.class).forEach(this::registerSubtypes); + } + + public static class UnformattedByteArraySerializer extends StdSerializer { + public UnformattedByteArraySerializer() { + super(byte[].class); + } + + @Override + public void serialize(byte[] value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + gen.writeString(DataConverter.bytesToRawHexString(value)); + } + } + + public static class UnformattedByteArrayDeserializer extends StdDeserializer { + public UnformattedByteArrayDeserializer() { + super(byte[].class); + } + + @Override + public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + return DataConverter.hexStringToByteArray(p.getText()); + } + } + + public static VisibilityChecker getFieldVisibilityChecker() { + return VisibilityChecker.Std.defaultInstance() + .withFieldVisibility(JsonAutoDetect.Visibility.ANY) + .withGetterVisibility(JsonAutoDetect.Visibility.NONE) + .withSetterVisibility(JsonAutoDetect.Visibility.NONE) + .withIsGetterVisibility(JsonAutoDetect.Visibility.NONE); + } +} diff --git a/src/main/java/de/rub/nds/modifiablevariable/json/ObjectMapperSubTypesResolver.java b/src/main/java/de/rub/nds/modifiablevariable/json/ObjectMapperSubTypesResolver.java new file mode 100644 index 00000000..ea4e5a4c --- /dev/null +++ b/src/main/java/de/rub/nds/modifiablevariable/json/ObjectMapperSubTypesResolver.java @@ -0,0 +1,411 @@ +/* + * ModifiableVariable - A Variable Concept for Runtime Modifications + * + * Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH + * + * Licensed under Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 + */ +package de.rub.nds.modifiablevariable.json; + +import com.fasterxml.classmate.ResolvedType; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.introspect.AnnotatedClass; +import com.fasterxml.jackson.databind.introspect.AnnotatedClassResolver; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.github.victools.jsonschema.generator.CustomDefinition; +import com.github.victools.jsonschema.generator.CustomDefinitionProviderV2; +import com.github.victools.jsonschema.generator.FieldScope; +import com.github.victools.jsonschema.generator.MethodScope; +import com.github.victools.jsonschema.generator.SchemaGenerationContext; +import com.github.victools.jsonschema.generator.SchemaKeyword; +import com.github.victools.jsonschema.generator.SubtypeResolver; +import com.github.victools.jsonschema.generator.TypeContext; +import com.github.victools.jsonschema.generator.TypeScope; +import com.github.victools.jsonschema.generator.impl.AttributeCollector; +import com.github.victools.jsonschema.module.jackson.JacksonOption; +import com.github.victools.jsonschema.module.jackson.JsonIdentityReferenceDefinitionProvider; +import java.lang.annotation.Annotation; +import java.lang.reflect.Modifier; +import java.util.*; +import java.util.stream.Collectors; +import org.apache.commons.lang3.NotImplementedException; + +/** + * Look-up of subtypes from a {@link ObjectMapper} instance. Based on {@link + * com.github.victools.jsonschema.module.jackson.JsonSubTypesResolver}. + */ +public class ObjectMapperSubTypesResolver implements SubtypeResolver, CustomDefinitionProviderV2 { + + private final CustomDefinition.DefinitionType wrappingSubtypeDefinitionType; + private final boolean shouldInlineNestedSubtypes; + private final Optional identityReferenceProvider; + private final ObjectMapper objectMapper; + + /** + * Constructor expecting list of enabled module options.
+ * Currently, only the {@link JacksonOption#ALWAYS_REF_SUBTYPES} is considered here. Other + * relevant options are handled by the module class. + * + * @param options module options to derive differing behavior from + */ + public ObjectMapperSubTypesResolver( + ObjectMapper objectMapper, Collection options) { + this.objectMapper = objectMapper; + this.wrappingSubtypeDefinitionType = + options.contains(JacksonOption.ALWAYS_REF_SUBTYPES) + ? CustomDefinition.DefinitionType.ALWAYS_REF + : CustomDefinition.DefinitionType.STANDARD; + this.shouldInlineNestedSubtypes = + options.contains(JacksonOption.INLINE_TRANSFORMED_SUBTYPES); + if (options.contains(JacksonOption.JSONIDENTITY_REFERENCE_ALWAYS_AS_ID)) { + this.identityReferenceProvider = + Optional.of(new JsonIdentityReferenceDefinitionProvider()); + } else { + this.identityReferenceProvider = Optional.empty(); + } + } + + /** + * Check whether to skip the subtype handling for a particular type (e.g. when {@code + * JacksonOption.JSONIDENTITY_REFERENCE_ALWAYS_AS_ID} applies instead). + * + * @param declaredType type for which a potential subtype should be resolved (or not) + * @param context applicable type context, that offers convenience methods, e.g., for the + * annotation look-up + * @return whether to skip the subtype resolution for the given type + */ + private boolean skipSubtypeResolution(ResolvedType declaredType, TypeContext context) { + return this.identityReferenceProvider + .flatMap( + idRefProvider -> + idRefProvider.getIdentityReferenceType(declaredType, context)) + .isPresent(); + } + + /* + * Looking-up declared subtypes for encountered supertype in general. + */ + @Override + public List findSubtypes( + ResolvedType declaredType, SchemaGenerationContext context) { + if (this.skipSubtypeResolution(declaredType, context.getTypeContext())) { + return null; + } + + // Check whether the type has a {@link JsonTypeInfo} annotation. + TypeContext typeContext = context.getTypeContext(); + ResolvedType typeWithTypeInfo = + typeContext.getTypeWithAnnotation( + declaredType, + JsonTypeInfo.class, + ReflectiveJacksonSchemaModule.NESTED_ANNOTATION_CHECK); + if (!declaredType.equals(typeWithTypeInfo)) { + return null; + } + + AnnotatedClass annotatedClass = + AnnotatedClassResolver.resolveWithoutSuperTypes( + objectMapper.getDeserializationConfig(), declaredType.getErasedType()); + Collection registeredSubtypes = + objectMapper + .getSubtypeResolver() + .collectAndResolveSubtypesByTypeId( + objectMapper.getDeserializationConfig(), annotatedClass); + + return registeredSubtypes.stream() + .map(NamedType::getType) + .map(typeContext::resolve) + // Do not include the declared type itself in the subtype list + .filter(resolvedType -> !resolvedType.equals(declaredType)) + // Do not resolve to interfaces or abstract types + .filter( + resolvedType -> + !resolvedType.getErasedType().isInterface() + && !Modifier.isAbstract( + resolvedType.getErasedType().getModifiers())) + // Do not include subtypes that do not have the same type bindings as the declared + // type + .filter( + resolvedType -> + filterSubtypesWithMatchingTypeBindings(declaredType, resolvedType)) + .sorted(Comparator.comparing(a -> a.getErasedType().getName())) + .collect(Collectors.toList()); + } + + /** + * Filter subtypes to ensure that they have the same type bindings as the declared type. + * + * @param declaredType the supertype for which subtypes are being resolved + * @param resolvedType the resolved subtype to check against the declared type + * @return true if the subtype has the same type bindings as the declared type, false otherwise + */ + public boolean filterSubtypesWithMatchingTypeBindings( + ResolvedType declaredType, ResolvedType resolvedType) { + List resolvedTypeParameters = + resolvedType.typeParametersFor(declaredType.getErasedType()); + if (resolvedTypeParameters == null) { + // If the resolved type does not have the declared type as a parent, return false + return false; + } + return declaredType.getTypeBindings().isEmpty() + || resolvedTypeParameters.equals( + declaredType.getTypeBindings().getTypeParameters()); + } + + /* + * Providing custom schema definition for subtype. + */ + @Override + public CustomDefinition provideCustomSchemaDefinition( + ResolvedType javaType, SchemaGenerationContext context) { + if (javaType == null) { + // since 4.37.0: not for void methods + return null; + } + final TypeContext typeContext = context.getTypeContext(); + ResolvedType typeWithTypeInfo = + typeContext.getTypeWithAnnotation( + javaType, + JsonTypeInfo.class, + ReflectiveJacksonSchemaModule.NESTED_ANNOTATION_CHECK); + if (typeWithTypeInfo == null + || javaType.equals(typeWithTypeInfo) + || this.skipSubtypeResolution(javaType, typeContext)) { + // no @JsonTypeInfo annotation found or the given javaType is the super type, that + // should be replaced + return null; + } + Class erasedTypeWithTypeInfo = typeWithTypeInfo.getErasedType(); + final List annotationsList = + Arrays.asList(erasedTypeWithTypeInfo.getAnnotations()); + JsonTypeInfo typeInfoAnnotation = + typeContext.getAnnotationFromList( + JsonTypeInfo.class, + annotationsList, + ReflectiveJacksonSchemaModule.NESTED_ANNOTATION_CHECK); + TypeScope scope = typeContext.createTypeScope(javaType); + ObjectNode definition = this.createSubtypeDefinition(scope, typeInfoAnnotation, context); + if (definition == null) { + return null; + } + return new CustomDefinition( + definition, + this.wrappingSubtypeDefinitionType, + CustomDefinition.AttributeInclusion.NO); + } + + /** + * Determine the appropriate type identifier according to {@link JsonTypeInfo#use()}. + * + * @param javaType specific subtype to identify + * @param typeInfoAnnotation annotation for determining what kind of identifier to use + * @return type identifier (or {@code null} if no supported value could be found) + */ + private String getTypeIdentifier(ResolvedType javaType, JsonTypeInfo typeInfoAnnotation) { + Class erasedTargetType = javaType.getErasedType(); + // Retrieve corresponding NamedType from the subtype resolver in the Jackson object mapper + return switch (typeInfoAnnotation.use()) { + case NAME -> + // TODO: Implement lookup of JsonTypeInfo.Id.NAME from object mapper + throw new NotImplementedException( + "Lookup of JsonTypeInfo.Id.NAME from object mapper is not yet supported"); + case SIMPLE_NAME -> erasedTargetType.getSimpleName(); + case CLASS -> erasedTargetType.getName(); + default -> null; + }; + } + + /** + * Create the custom schema definition for the given subtype, considering the {@link + * JsonTypeInfo#include()} setting. + * + * @param scope targeted subtype + * @param typeInfoAnnotation annotation for looking up the type identifier and determining the + * kind of inclusion/serialization + * @param context generation context + * @return created custom definition (or {@code null} if no supported subtype resolution + * scenario could be detected + */ + private ObjectNode createSubtypeDefinition( + TypeScope scope, JsonTypeInfo typeInfoAnnotation, SchemaGenerationContext context) { + ResolvedType javaType = scope.getType(); + final String typeIdentifier = this.getTypeIdentifier(javaType, typeInfoAnnotation); + if (typeIdentifier == null) { + return null; + } + ObjectNode attributesToInclude = this.getAttributesToInclude(scope, context); + final ObjectNode definition = context.getGeneratorConfig().createObjectNode(); + SubtypeDefinitionDetails subtypeDetails = + new SubtypeDefinitionDetails( + javaType, attributesToInclude, context, typeIdentifier, definition); + switch (typeInfoAnnotation.include()) { + case WRAPPER_ARRAY: + createSubtypeDefinitionForWrapperArrayTypeInfo(subtypeDetails); + break; + case WRAPPER_OBJECT: + this.createSubtypeDefinitionForWrapperObjectTypeInfo(subtypeDetails); + break; + case PROPERTY: + case EXISTING_PROPERTY: + this.createSubtypeDefinitionForPropertyTypeInfo(subtypeDetails, typeInfoAnnotation); + break; + default: + return null; + } + return definition; + } + + private void createSubtypeDefinitionForWrapperArrayTypeInfo(SubtypeDefinitionDetails details) { + details.getDefinition() + .put( + details.getKeyword(SchemaKeyword.TAG_TYPE), + details.getKeyword(SchemaKeyword.TAG_TYPE_ARRAY)); + ArrayNode itemsArray = + details.getDefinition() + .withArray(details.getKeyword(SchemaKeyword.TAG_PREFIX_ITEMS)); + itemsArray + .addObject() + .put( + details.getKeyword(SchemaKeyword.TAG_TYPE), + details.getKeyword(SchemaKeyword.TAG_TYPE_STRING)) + .put(details.getKeyword(SchemaKeyword.TAG_CONST), details.getTypeIdentifier()); + if (details.getAttributesToInclude() == null + || details.getAttributesToInclude().isEmpty()) { + itemsArray.add( + this.createNestedSubtypeSchema(details.getJavaType(), details.getContext())); + } else { + itemsArray + .addObject() + .withArray(details.getKeyword(SchemaKeyword.TAG_ALLOF)) + .add( + this.createNestedSubtypeSchema( + details.getJavaType(), details.getContext())) + .add(details.getAttributesToInclude()); + } + } + + private void createSubtypeDefinitionForWrapperObjectTypeInfo(SubtypeDefinitionDetails details) { + details.getDefinition() + .put( + details.getKeyword(SchemaKeyword.TAG_TYPE), + details.getKeyword(SchemaKeyword.TAG_TYPE_OBJECT)); + ObjectNode propertiesNode = + details.getDefinition().putObject(details.getKeyword(SchemaKeyword.TAG_PROPERTIES)); + ObjectNode nestedSubtypeSchema = + this.createNestedSubtypeSchema(details.getJavaType(), details.getContext()); + if (details.getAttributesToInclude() == null + || details.getAttributesToInclude().isEmpty()) { + propertiesNode.set(details.getTypeIdentifier(), nestedSubtypeSchema); + } else { + propertiesNode + .putObject(details.getTypeIdentifier()) + .withArray(details.getKeyword(SchemaKeyword.TAG_ALLOF)) + .add(nestedSubtypeSchema) + .add(details.getAttributesToInclude()); + } + details.getDefinition() + .withArray(details.getKeyword(SchemaKeyword.TAG_REQUIRED)) + .add(details.getTypeIdentifier()); + } + + private void createSubtypeDefinitionForPropertyTypeInfo( + SubtypeDefinitionDetails details, JsonTypeInfo typeInfoAnnotation) { + final String propertyName = + Optional.ofNullable(typeInfoAnnotation.property()) + .filter(name -> !name.isEmpty()) + .orElseGet(() -> typeInfoAnnotation.use().getDefaultPropertyName()); + ObjectNode additionalPart = + details.getDefinition() + .withArray(details.getKeyword(SchemaKeyword.TAG_ALLOF)) + .add( + this.createNestedSubtypeSchema( + details.getJavaType(), details.getContext())) + .addObject(); + if (details.getAttributesToInclude() != null + && !details.getAttributesToInclude().isEmpty()) { + additionalPart.setAll(details.getAttributesToInclude()); + } + additionalPart + .put( + details.getKeyword(SchemaKeyword.TAG_TYPE), + details.getKeyword(SchemaKeyword.TAG_TYPE_OBJECT)) + .putObject(details.getKeyword(SchemaKeyword.TAG_PROPERTIES)) + .putObject(propertyName) + .put(details.getKeyword(SchemaKeyword.TAG_CONST), details.getTypeIdentifier()); + if (!details.getJavaType().getErasedType().equals(typeInfoAnnotation.defaultImpl())) { + additionalPart + .withArray(details.getKeyword(SchemaKeyword.TAG_REQUIRED)) + .add(propertyName); + } + } + + private ObjectNode createNestedSubtypeSchema( + ResolvedType javaType, SchemaGenerationContext context) { + if (this.shouldInlineNestedSubtypes) { + return context.createStandardDefinition(javaType, this); + } + return context.createStandardDefinitionReference(javaType, this); + } + + private ObjectNode getAttributesToInclude(TypeScope scope, SchemaGenerationContext context) { + ObjectNode attributesToInclude; + if (scope instanceof FieldScope) { + attributesToInclude = + AttributeCollector.collectFieldAttributes((FieldScope) scope, context); + } else if (scope instanceof MethodScope) { + attributesToInclude = + AttributeCollector.collectMethodAttributes((MethodScope) scope, context); + } else { + attributesToInclude = null; + } + return attributesToInclude; + } + + private static class SubtypeDefinitionDetails { + private final ResolvedType javaType; + private final ObjectNode attributesToInclude; + private final SchemaGenerationContext context; + private final String typeIdentifier; + private final ObjectNode definition; + + SubtypeDefinitionDetails( + ResolvedType javaType, + ObjectNode attributesToInclude, + SchemaGenerationContext context, + String typeIdentifier, + ObjectNode definition) { + this.javaType = javaType; + this.attributesToInclude = attributesToInclude; + this.context = context; + this.typeIdentifier = typeIdentifier; + this.definition = definition; + } + + ResolvedType getJavaType() { + return this.javaType; + } + + ObjectNode getAttributesToInclude() { + return this.attributesToInclude; + } + + SchemaGenerationContext getContext() { + return this.context; + } + + String getTypeIdentifier() { + return this.typeIdentifier; + } + + ObjectNode getDefinition() { + return this.definition; + } + + String getKeyword(SchemaKeyword keyword) { + return this.context.getKeyword(keyword); + } + } +} diff --git a/src/main/java/de/rub/nds/modifiablevariable/json/ReflectiveJacksonSchemaModule.java b/src/main/java/de/rub/nds/modifiablevariable/json/ReflectiveJacksonSchemaModule.java new file mode 100644 index 00000000..2142c3b0 --- /dev/null +++ b/src/main/java/de/rub/nds/modifiablevariable/json/ReflectiveJacksonSchemaModule.java @@ -0,0 +1,443 @@ +/* + * ModifiableVariable - A Variable Concept for Runtime Modifications + * + * Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH + * + * Licensed under Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 + */ +package de.rub.nds.modifiablevariable.json; + +import com.fasterxml.classmate.ResolvedType; +import com.fasterxml.classmate.members.HierarchicType; +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonClassDescription; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.github.victools.jsonschema.generator.AnnotationHelper; +import com.github.victools.jsonschema.generator.FieldScope; +import com.github.victools.jsonschema.generator.MemberScope; +import com.github.victools.jsonschema.generator.MethodScope; +import com.github.victools.jsonschema.generator.Module; +import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder; +import com.github.victools.jsonschema.generator.SchemaGeneratorConfigPart; +import com.github.victools.jsonschema.generator.SchemaGeneratorGeneralConfigPart; +import com.github.victools.jsonschema.generator.TypeScope; +import com.github.victools.jsonschema.module.jackson.*; +import java.lang.annotation.Annotation; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; + +/** + * Module for setting up schema generation aspects based on {@code jackson-annotations}. Supports + * reflective resolution of subtypes via the object mapper. Based on {@link + * com.github.victools.jsonschema.module.jackson.JacksonModule}. + * + *
    + *
  • Populate the "description" attributes as per {@link JsonPropertyDescription} and {@link + * JsonClassDescription} annotations. + *
  • Apply alternative property names defined in {@link JsonProperty} annotations. + *
  • Exclude properties that are deemed to be ignored per the various annotations for that + * purpose. + *
  • Optionally: treat enum types as plain strings as per {@link + * com.fasterxml.jackson.annotation.JsonValue JsonValue} annotations. + *
+ */ +public class ReflectiveJacksonSchemaModule implements Module { + + static final Predicate NESTED_ANNOTATION_CHECK = + annotation -> + annotation.annotationType().isAnnotationPresent(JacksonAnnotationsInside.class); + + private final Set options; + private ObjectMapper objectMapper; + private final Map, BeanDescription> beanDescriptions = + Collections.synchronizedMap(new HashMap<>()); + private final Map, PropertyNamingStrategy> namingStrategies = + Collections.synchronizedMap(new HashMap<>()); + + /** + * Constructor, without any additional options. + * + * @see ReflectiveJacksonSchemaModule#ReflectiveJacksonSchemaModule(JacksonOption...) + */ + public ReflectiveJacksonSchemaModule() { + // Default options + this.options = + Set.of( + JacksonOption.ALWAYS_REF_SUBTYPES, + JacksonOption.RESPECT_JSONPROPERTY_REQUIRED); + } + + /** + * Constructor. + * + * @param options features to enable + */ + public ReflectiveJacksonSchemaModule(JacksonOption... options) { + this.options = + options == null ? Collections.emptySet() : new HashSet<>(Arrays.asList(options)); + } + + @Override + public void applyToConfigBuilder(SchemaGeneratorConfigBuilder builder) { + this.objectMapper = builder.getObjectMapper(); + SchemaGeneratorConfigPart fieldConfigPart = builder.forFields(); + SchemaGeneratorConfigPart methodConfigPart = builder.forMethods(); + + this.applyToConfigBuilderPart(fieldConfigPart); + this.applyToConfigBuilderPart(methodConfigPart); + + fieldConfigPart.withIgnoreCheck(this::shouldIgnoreField); + methodConfigPart.withIgnoreCheck(this::shouldIgnoreMethod); + if (!this.options.contains(JacksonOption.IGNORE_PROPERTY_NAMING_STRATEGY)) { + // only consider @JsonNaming as fall-back + fieldConfigPart.withPropertyNameOverrideResolver( + this::getPropertyNameOverrideBasedOnJsonNamingAnnotation); + } + + SchemaGeneratorGeneralConfigPart generalConfigPart = builder.forTypesInGeneral(); + generalConfigPart.withDescriptionResolver(this::resolveDescriptionForType); + + boolean considerEnumJsonValue = + this.options.contains(JacksonOption.FLATTENED_ENUMS_FROM_JSONVALUE); + boolean considerEnumJsonProperty = + this.options.contains(JacksonOption.FLATTENED_ENUMS_FROM_JSONPROPERTY); + if (considerEnumJsonValue || considerEnumJsonProperty) { + generalConfigPart.withCustomDefinitionProvider( + new CustomEnumDefinitionProvider( + considerEnumJsonValue, considerEnumJsonProperty)); + } + + if (this.options.contains(JacksonOption.RESPECT_JSONPROPERTY_ORDER)) { + generalConfigPart.withPropertySorter(new JsonPropertySorter(true)); + } + if (this.options.contains(JacksonOption.JSONIDENTITY_REFERENCE_ALWAYS_AS_ID)) { + JsonIdentityReferenceDefinitionProvider identityReferenceDefinitionProvider = + new JsonIdentityReferenceDefinitionProvider(); + generalConfigPart.withCustomDefinitionProvider(identityReferenceDefinitionProvider); + fieldConfigPart.withCustomDefinitionProvider( + identityReferenceDefinitionProvider::provideCustomPropertySchemaDefinition); + methodConfigPart.withCustomDefinitionProvider( + identityReferenceDefinitionProvider::provideCustomPropertySchemaDefinition); + } + + applySubtypeResolverToConfigBuilder(generalConfigPart, fieldConfigPart, methodConfigPart); + + generalConfigPart.withCustomDefinitionProvider(new JsonUnwrappedDefinitionProvider()); + } + + private void applySubtypeResolverToConfigBuilder( + SchemaGeneratorGeneralConfigPart generalConfigPart, + SchemaGeneratorConfigPart fieldConfigPart, + SchemaGeneratorConfigPart methodConfigPart) { + boolean skipLookUpSubtypes = this.options.contains(JacksonOption.SKIP_SUBTYPE_LOOKUP); + boolean skipTypeInfoTransform = + this.options.contains(JacksonOption.IGNORE_TYPE_INFO_TRANSFORM); + if (skipLookUpSubtypes && skipTypeInfoTransform) { + return; + } + ObjectMapperSubTypesResolver subtypeResolver = + new ObjectMapperSubTypesResolver(this.objectMapper, this.options); + if (!skipLookUpSubtypes) { + generalConfigPart.withSubtypeResolver(subtypeResolver); + } + if (!skipTypeInfoTransform) { + generalConfigPart.withCustomDefinitionProvider(subtypeResolver); + } + } + + /** + * Apply common member configurations. + * + * @param configPart config builder part for either fields or methods + */ + private void applyToConfigBuilderPart(SchemaGeneratorConfigPart configPart) { + configPart.withDescriptionResolver(this::resolveDescription); + configPart.withPropertyNameOverrideResolver( + this::getPropertyNameOverrideBasedOnJsonPropertyAnnotation); + configPart.withReadOnlyCheck(this::getReadOnlyCheck); + configPart.withWriteOnlyCheck(this::getWriteOnlyCheck); + + if (this.options.contains(JacksonOption.RESPECT_JSONPROPERTY_REQUIRED)) { + configPart.withRequiredCheck(this::getRequiredCheckBasedOnJsonPropertyAnnotation); + } + } + + /** + * Determine the given member's associated "description" in the following order of priority. + * + *
    + *
  1. {@link JsonPropertyDescription} annotation on the field/method itself + *
  2. {@link JsonPropertyDescription} annotation on the field's getter method or the getter + * method's associated field + *
+ * + * @param member field/method for which to collect an available description + * @return successfully looked-up description (or {@code null}) + */ + protected String resolveDescription(MemberScope member) { + // look for property specific description + JsonPropertyDescription propertyAnnotation = + member.getAnnotationConsideringFieldAndGetterIfSupported( + JsonPropertyDescription.class, NESTED_ANNOTATION_CHECK); + if (propertyAnnotation != null) { + return propertyAnnotation.value(); + } + return null; + } + + /** + * Determine the given type's associated "description" via the following annotation. + * + *
    + *
  • {@link JsonClassDescription} annotation on the targeted type's class + *
+ * + * @param scope scope for which to collect an available description + * @return successfully looked-up description (or {@code null}) + */ + protected String resolveDescriptionForType(TypeScope scope) { + Class rawType = scope.getType().getErasedType(); + return AnnotationHelper.resolveAnnotation( + rawType, JsonClassDescription.class, NESTED_ANNOTATION_CHECK) + .map(JsonClassDescription::value) + .orElse(null); + } + + /** + * Look-up an alternative name as per the following order of priority. + * + *
    + *
  1. {@link JsonProperty} annotation on the member itself + *
  2. {@link JsonProperty} annotation on the field's getter method or the getter method's + * associated field + *
+ * + * @param member field/method to look-up alternative property name for + * @return alternative property name (or {@code null}) + */ + protected String getPropertyNameOverrideBasedOnJsonPropertyAnnotation( + MemberScope member) { + JsonProperty annotation = + member.getAnnotationConsideringFieldAndGetter( + JsonProperty.class, NESTED_ANNOTATION_CHECK); + if (annotation != null) { + String nameOverride = annotation.value(); + // check for invalid overrides + if (nameOverride != null + && !nameOverride.isEmpty() + && !nameOverride.equals(member.getDeclaredName())) { + return nameOverride; + } + } + return null; + } + + /** + * Alter the declaring name of the given field as per the declaring type's {@link JsonNaming} + * annotation. + * + * @param field field to look-up naming strategy for + * @return altered property name (or {@code null}) + */ + protected String getPropertyNameOverrideBasedOnJsonNamingAnnotation(FieldScope field) { + final PropertyNamingStrategy strategy; + synchronized (this.namingStrategies) { + strategy = + this.namingStrategies.computeIfAbsent( + field.getDeclaringType().getErasedType(), + this::getAnnotatedNamingStrategy); + } + if (strategy == null) { + return null; + } + return strategy.nameForField(null, null, field.getName()); + } + + /** + * Look-up the given type's {@link JsonNaming} annotation and instantiate the declared {@link + * PropertyNamingStrategy}. + * + * @param declaringType type declaring fields for which the applicable naming strategy should be + * looked-up + * @return annotated naming strategy instance (or {@code null}) + */ + private PropertyNamingStrategy getAnnotatedNamingStrategy(Class declaringType) { + return AnnotationHelper.resolveAnnotation( + declaringType, JsonNaming.class, NESTED_ANNOTATION_CHECK) + .map(JsonNaming::value) + .map( + strategyType -> { + try { + return strategyType.getConstructor().newInstance(); + } catch (ReflectiveOperationException | SecurityException ex) { + return null; + } + }) + .orElse(null); + } + + /** + * Create a jackson {@link BeanDescription} for the given type's erased class in order to avoid + * having to re-create the complexity therein.
+ * This is assumed to have a negative performance impact (as one type is being introspected + * twice), that should be fine for schema generation. + * + * @param targetType type for whose erased class the {@link BeanDescription} should be created + * @return introspection result of given type's erased class + */ + protected final BeanDescription getBeanDescriptionForClass(ResolvedType targetType) { + // use a map to cater for some caching (and thereby performance improvement) + synchronized (this.beanDescriptions) { + return this.beanDescriptions.computeIfAbsent( + targetType.getErasedType(), + type -> + this.objectMapper + .getSerializationConfig() + .introspect( + this.objectMapper + .getTypeFactory() + .constructType(type))); + } + } + + /** + * Determine whether a given field should be ignored, according to various jackson annotations + * for that purpose,
+ * e.g. {@code JsonBackReference}, {@code JsonIgnore}, {@code JsonIgnoreType}, {@code + * JsonIgnoreProperties} + * + * @param field field to check + * @return whether field should be excluded + */ + protected boolean shouldIgnoreField(FieldScope field) { + if (field.getAnnotationConsideringFieldAndGetterIfSupported( + JsonBackReference.class, NESTED_ANNOTATION_CHECK) + != null) { + return true; + } + // @since 4.32.0 + JsonUnwrapped unwrappedAnnotation = + field.getAnnotationConsideringFieldAndGetterIfSupported( + JsonUnwrapped.class, NESTED_ANNOTATION_CHECK); + if (unwrappedAnnotation != null && unwrappedAnnotation.enabled()) { + // unwrapped properties should be ignored here, as they are included in their unwrapped + // form + return true; + } + // instead of re-creating the various ways a property may be included/excluded in jackson: + // just use its built-in introspection + HierarchicType topMostHierarchyType = + field.getDeclaringTypeMembers().allTypesAndOverrides().get(0); + BeanDescription beanDescription = + this.getBeanDescriptionForClass(topMostHierarchyType.getType()); + // some kinds of field ignorals are only available via an annotation introspector + Set ignoredProperties = + this.objectMapper + .getSerializationConfig() + .getAnnotationIntrospector() + .findPropertyIgnoralByName(null, beanDescription.getClassInfo()) + .getIgnored(); + String declaredName = field.getDeclaredName(); + if (ignoredProperties.contains(declaredName)) { + return true; + } + // @since 4.37.0 also consider overridden property name as it may match the getter method + String fieldName = field.getName(); + // other kinds of field ignorals are handled implicitly, i.e. are only available by way of + // being absent + return beanDescription.findProperties().stream() + .noneMatch( + propertyDefinition -> + declaredName.equals(propertyDefinition.getInternalName()) + || fieldName.equals(propertyDefinition.getInternalName())); + } + + /** + * Determine whether a given method should be ignored, according to various jackson annotations + * for that purpose,
+ * e.g. {@code JsonBackReference}, {@code JsonIgnore}, {@code JsonIgnoreType}, {@code + * JsonIgnoreProperties} + * + * @param method method to check + * @return whether method should be excluded + */ + protected boolean shouldIgnoreMethod(MethodScope method) { + FieldScope getterField = method.findGetterField(); + if (getterField == null) { + if (method.getAnnotationConsideringFieldAndGetterIfSupported( + JsonBackReference.class, NESTED_ANNOTATION_CHECK) + != null) { + return true; + } + // @since 4.32.0 + JsonUnwrapped unwrapped = + method.getAnnotationConsideringFieldAndGetterIfSupported( + JsonUnwrapped.class, NESTED_ANNOTATION_CHECK); + if (unwrapped != null && unwrapped.enabled()) { + // unwrapped properties should be ignored here, as they are included in their + // unwrapped form + return true; + } + } else if (this.shouldIgnoreField(getterField)) { + return true; + } + return this.options.contains(JacksonOption.INCLUDE_ONLY_JSONPROPERTY_ANNOTATED_METHODS) + && method.getAnnotationConsideringFieldAndGetter( + JsonProperty.class, NESTED_ANNOTATION_CHECK) + == null; + } + + /** + * Look-up the given field's/method's {@link JsonProperty} annotation and consider its + * "required" attribute. + * + * @param member field/method to look-up required strategy for + * @return whether the field should be in the "required" list or not + */ + protected boolean getRequiredCheckBasedOnJsonPropertyAnnotation(MemberScope member) { + JsonProperty jsonProperty = + member.getAnnotationConsideringFieldAndGetterIfSupported( + JsonProperty.class, NESTED_ANNOTATION_CHECK); + return jsonProperty != null && jsonProperty.required(); + } + + /** + * Determine whether the given field's/method's {@link JsonProperty} annotation marks it as + * read-only. + * + * @param member field/method to check read-only status for + * @return whether the field should be marked as read-only + */ + protected boolean getReadOnlyCheck(MemberScope member) { + JsonProperty jsonProperty = + member.getAnnotationConsideringFieldAndGetter( + JsonProperty.class, NESTED_ANNOTATION_CHECK); + return jsonProperty != null && jsonProperty.access() == JsonProperty.Access.READ_ONLY; + } + + /** + * Determine whether the given field's/method's {@link JsonProperty} annotation marks it as + * write-only. + * + * @param member field/method to check write-only status for + * @return whether the field should be marked as write-only + */ + protected boolean getWriteOnlyCheck(MemberScope member) { + JsonProperty jsonProperty = + member.getAnnotationConsideringFieldAndGetter( + JsonProperty.class, NESTED_ANNOTATION_CHECK); + return jsonProperty != null && jsonProperty.access() == JsonProperty.Access.WRITE_ONLY; + } +} diff --git a/src/main/java/de/rub/nds/modifiablevariable/length/ModifiableLengthField.java b/src/main/java/de/rub/nds/modifiablevariable/length/ModifiableLengthField.java index 01323ad8..4ff46c34 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/length/ModifiableLengthField.java +++ b/src/main/java/de/rub/nds/modifiablevariable/length/ModifiableLengthField.java @@ -7,6 +7,8 @@ */ package de.rub.nds.modifiablevariable.length; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.bytearray.ModifiableByteArray; import de.rub.nds.modifiablevariable.integer.ModifiableInteger; import jakarta.xml.bind.annotation.XmlAccessType; @@ -34,6 +36,8 @@ public class ModifiableLengthField extends ModifiableInteger { /** The byte array whose length this field represents */ + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonProperty(required = true) private final ModifiableByteArray ref; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/longint/LongAddModification.java b/src/main/java/de/rub/nds/modifiablevariable/longint/LongAddModification.java index f5ab0e25..6c5cfd24 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/longint/LongAddModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/longint/LongAddModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.longint; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.util.Objects; @@ -23,6 +24,7 @@ public class LongAddModification extends VariableModification { /** The value to add to the original long */ + @JsonProperty(required = true) private long summand; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/longint/LongExplicitValueModification.java b/src/main/java/de/rub/nds/modifiablevariable/longint/LongExplicitValueModification.java index b97a9932..72cb40f2 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/longint/LongExplicitValueModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/longint/LongExplicitValueModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.longint; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.util.Objects; @@ -24,6 +25,7 @@ public class LongExplicitValueModification extends VariableModification { /** The explicit value that will replace the original value */ + @JsonProperty(required = true) protected long explicitValue; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/longint/LongMultiplyModification.java b/src/main/java/de/rub/nds/modifiablevariable/longint/LongMultiplyModification.java index acdbe8db..771c1f18 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/longint/LongMultiplyModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/longint/LongMultiplyModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.longint; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.util.Objects; @@ -23,6 +24,7 @@ public class LongMultiplyModification extends VariableModification { /** The factor to multiply by */ + @JsonProperty(required = true) private long factor; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/longint/LongShiftLeftModification.java b/src/main/java/de/rub/nds/modifiablevariable/longint/LongShiftLeftModification.java index 0c4e2eaa..8c47ad0f 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/longint/LongShiftLeftModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/longint/LongShiftLeftModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.longint; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.util.Objects; @@ -25,6 +26,7 @@ public class LongShiftLeftModification extends VariableModification { /** The number of bit positions to shift left */ + @JsonProperty(required = true) private int shift; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/longint/LongShiftRightModification.java b/src/main/java/de/rub/nds/modifiablevariable/longint/LongShiftRightModification.java index b9ba91fd..3407f624 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/longint/LongShiftRightModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/longint/LongShiftRightModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.longint; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.util.Objects; @@ -25,6 +26,7 @@ public class LongShiftRightModification extends VariableModification { /** The number of bit positions to shift right */ + @JsonProperty(required = true) private int shift; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/longint/LongSubtractModification.java b/src/main/java/de/rub/nds/modifiablevariable/longint/LongSubtractModification.java index ed679025..974b9d0e 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/longint/LongSubtractModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/longint/LongSubtractModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.longint; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.util.Objects; @@ -24,6 +25,7 @@ public class LongSubtractModification extends VariableModification { /** The value to subtract from the original long */ + @JsonProperty(required = true) private long subtrahend; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/longint/LongXorModification.java b/src/main/java/de/rub/nds/modifiablevariable/longint/LongXorModification.java index 564328c6..155c32b4 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/longint/LongXorModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/longint/LongXorModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.longint; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.util.Objects; @@ -24,6 +25,7 @@ public class LongXorModification extends VariableModification { /** The XOR mask to apply to the original long */ + @JsonProperty(required = true) private long xor; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/longint/ModifiableLong.java b/src/main/java/de/rub/nds/modifiablevariable/longint/ModifiableLong.java index 38f515bd..118b44c8 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/longint/ModifiableLong.java +++ b/src/main/java/de/rub/nds/modifiablevariable/longint/ModifiableLong.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.longint; +import com.fasterxml.jackson.annotation.JsonInclude; import de.rub.nds.modifiablevariable.ModifiableVariable; import de.rub.nds.modifiablevariable.util.DataConverter; import jakarta.xml.bind.annotation.XmlRootElement; @@ -37,6 +38,7 @@ public class ModifiableLong extends ModifiableVariable { /** The original, unmodified value of this variable */ + @JsonInclude(JsonInclude.Include.NON_NULL) private Long originalValue; /** diff --git a/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ByteAddModification.java b/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ByteAddModification.java index 0d2026b9..93b84fa1 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ByteAddModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ByteAddModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.singlebyte; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.util.Objects; @@ -24,6 +25,7 @@ public class ByteAddModification extends VariableModification { /** The value to be added to the original byte */ + @JsonProperty(required = true) private byte summand; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ByteExplicitValueModification.java b/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ByteExplicitValueModification.java index 7f147a0c..1d08dc8f 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ByteExplicitValueModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ByteExplicitValueModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.singlebyte; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.util.Objects; @@ -23,6 +24,7 @@ public class ByteExplicitValueModification extends VariableModification { /** The value that will replace the original byte */ + @JsonProperty(required = true) protected byte explicitValue; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ByteSubtractModification.java b/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ByteSubtractModification.java index 5cf6992f..143333e7 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ByteSubtractModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ByteSubtractModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.singlebyte; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.util.Objects; @@ -23,6 +24,7 @@ public class ByteSubtractModification extends VariableModification { /** The value to be subtracted from the original byte */ + @JsonProperty(required = true) private byte subtrahend; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ByteXorModification.java b/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ByteXorModification.java index 61b70ba0..5efb6acd 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ByteXorModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ByteXorModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.singlebyte; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.util.Objects; @@ -23,6 +24,7 @@ public class ByteXorModification extends VariableModification { /** The byte value to XOR with the input byte */ + @JsonProperty(required = true) private byte xor; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ModifiableByte.java b/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ModifiableByte.java index 057106bf..1b1c363f 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ModifiableByte.java +++ b/src/main/java/de/rub/nds/modifiablevariable/singlebyte/ModifiableByte.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.singlebyte; +import com.fasterxml.jackson.annotation.JsonInclude; import de.rub.nds.modifiablevariable.ModifiableVariable; import jakarta.xml.bind.annotation.XmlRootElement; @@ -24,6 +25,7 @@ public class ModifiableByte extends ModifiableVariable { /** The original byte value before any modifications */ + @JsonInclude(JsonInclude.Include.NON_NULL) private Byte originalValue; /** Default constructor that creates an empty ModifiableByte with no original value. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/string/ModifiableString.java b/src/main/java/de/rub/nds/modifiablevariable/string/ModifiableString.java index a703ef58..a09f6011 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/string/ModifiableString.java +++ b/src/main/java/de/rub/nds/modifiablevariable/string/ModifiableString.java @@ -9,6 +9,7 @@ import static de.rub.nds.modifiablevariable.util.StringUtil.backslashEscapeString; +import com.fasterxml.jackson.annotation.JsonInclude; import de.rub.nds.modifiablevariable.ModifiableVariable; import de.rub.nds.modifiablevariable.util.IllegalStringAdapter; import jakarta.xml.bind.annotation.XmlAccessType; @@ -38,6 +39,7 @@ public class ModifiableString extends ModifiableVariable { /** The original string value before any modifications */ + @JsonInclude(JsonInclude.Include.NON_NULL) protected String originalValue; /** Default constructor that creates an empty ModifiableString with no original value. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/string/StringAppendValueModification.java b/src/main/java/de/rub/nds/modifiablevariable/string/StringAppendValueModification.java index 50346cf3..43aadb99 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/string/StringAppendValueModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/string/StringAppendValueModification.java @@ -9,6 +9,7 @@ import static de.rub.nds.modifiablevariable.util.StringUtil.backslashEscapeString; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import de.rub.nds.modifiablevariable.util.IllegalStringAdapter; import jakarta.xml.bind.annotation.XmlRootElement; @@ -30,6 +31,7 @@ public class StringAppendValueModification extends VariableModification /** The string value to append to the input */ @XmlJavaTypeAdapter(IllegalStringAdapter.class) + @JsonProperty(required = true) private String appendValue; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/string/StringDeleteModification.java b/src/main/java/de/rub/nds/modifiablevariable/string/StringDeleteModification.java index 9fa6f852..9a693e6d 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/string/StringDeleteModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/string/StringDeleteModification.java @@ -7,6 +7,7 @@ */ package de.rub.nds.modifiablevariable.string; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import jakarta.xml.bind.annotation.XmlRootElement; import java.util.Objects; @@ -25,9 +26,11 @@ public class StringDeleteModification extends VariableModification { /** The number of characters to delete */ + @JsonProperty(required = true) private int count; /** The position from which to start deletion (0-based index) */ + @JsonProperty(required = true) private int startPosition; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/string/StringExplicitValueModification.java b/src/main/java/de/rub/nds/modifiablevariable/string/StringExplicitValueModification.java index a18fabb0..b8c796b5 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/string/StringExplicitValueModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/string/StringExplicitValueModification.java @@ -9,6 +9,7 @@ import static de.rub.nds.modifiablevariable.util.StringUtil.backslashEscapeString; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import de.rub.nds.modifiablevariable.util.IllegalStringAdapter; import jakarta.xml.bind.annotation.XmlRootElement; @@ -29,6 +30,7 @@ public class StringExplicitValueModification extends VariableModification /** The string to insert into the original string */ @XmlJavaTypeAdapter(IllegalStringAdapter.class) + @JsonProperty(required = true) private String insertValue; /** The position at which to insert the string (0-based index) */ + @JsonProperty(required = true) private int startPosition; /** Default constructor for serialization. */ diff --git a/src/main/java/de/rub/nds/modifiablevariable/string/StringPrependValueModification.java b/src/main/java/de/rub/nds/modifiablevariable/string/StringPrependValueModification.java index 3efc081b..dc3e4157 100644 --- a/src/main/java/de/rub/nds/modifiablevariable/string/StringPrependValueModification.java +++ b/src/main/java/de/rub/nds/modifiablevariable/string/StringPrependValueModification.java @@ -9,6 +9,7 @@ import static de.rub.nds.modifiablevariable.util.StringUtil.backslashEscapeString; +import com.fasterxml.jackson.annotation.JsonProperty; import de.rub.nds.modifiablevariable.VariableModification; import de.rub.nds.modifiablevariable.util.IllegalStringAdapter; import jakarta.xml.bind.annotation.XmlRootElement; @@ -31,6 +32,7 @@ public class StringPrependValueModification extends VariableModification /** The string value to be prepended to the original string */ @XmlJavaTypeAdapter(IllegalStringAdapter.class) + @JsonProperty(required = true) private String prependValue; /** Default constructor for serialization. */ diff --git a/src/main/resources/ModifiableVariable.schema.json b/src/main/resources/ModifiableVariable.schema.json new file mode 100644 index 00000000..f2befd5f --- /dev/null +++ b/src/main/resources/ModifiableVariable.schema.json @@ -0,0 +1,824 @@ +{ + "$schema" : "https://json-schema.org/draft/2020-12/schema", + "$defs" : { + "BigIntegerAddModification" : { + "type" : "object", + "properties" : { + "summand" : { + "type" : "integer" + }, + "@type" : { + "const" : "BigIntegerAddModification" + } + }, + "required" : [ "summand", "@type" ] + }, + "BigIntegerExplicitValueModification" : { + "type" : "object", + "properties" : { + "explicitValue" : { + "type" : "integer" + }, + "@type" : { + "const" : "BigIntegerExplicitValueModification" + } + }, + "required" : [ "explicitValue", "@type" ] + }, + "BigIntegerMultiplyModification" : { + "type" : "object", + "properties" : { + "factor" : { + "type" : "integer" + }, + "@type" : { + "const" : "BigIntegerMultiplyModification" + } + }, + "required" : [ "factor", "@type" ] + }, + "BigIntegerShiftLeftModification" : { + "type" : "object", + "properties" : { + "shift" : { + "type" : "integer" + }, + "@type" : { + "const" : "BigIntegerShiftLeftModification" + } + }, + "required" : [ "shift", "@type" ] + }, + "BigIntegerShiftRightModification" : { + "type" : "object", + "properties" : { + "shift" : { + "type" : "integer" + }, + "@type" : { + "const" : "BigIntegerShiftRightModification" + } + }, + "required" : [ "shift", "@type" ] + }, + "BigIntegerSubtractModification" : { + "type" : "object", + "properties" : { + "subtrahend" : { + "type" : "integer" + }, + "@type" : { + "const" : "BigIntegerSubtractModification" + } + }, + "required" : [ "subtrahend", "@type" ] + }, + "BigIntegerXorModification" : { + "type" : "object", + "properties" : { + "xor" : { + "type" : "integer" + }, + "@type" : { + "const" : "BigIntegerXorModification" + } + }, + "required" : [ "xor", "@type" ] + }, + "BooleanExplicitValueModification" : { + "type" : "object", + "properties" : { + "explicitValue" : { + "type" : "boolean" + }, + "@type" : { + "const" : "BooleanExplicitValueModification" + } + }, + "required" : [ "explicitValue", "@type" ] + }, + "BooleanToggleModification" : { + "type" : "object", + "properties" : { + "@type" : { + "const" : "BooleanToggleModification" + } + }, + "required" : [ "@type" ] + }, + "ByteAddModification" : { + "type" : "object", + "properties" : { + "summand" : { + "type" : "string" + }, + "@type" : { + "const" : "ByteAddModification" + } + }, + "required" : [ "summand", "@type" ] + }, + "ByteArrayAppendValueModification" : { + "type" : "object", + "properties" : { + "bytesToAppend" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "@type" : { + "const" : "ByteArrayAppendValueModification" + } + }, + "required" : [ "bytesToAppend", "@type" ] + }, + "ByteArrayDeleteModification" : { + "type" : "object", + "properties" : { + "count" : { + "type" : "integer" + }, + "startPosition" : { + "type" : "integer" + }, + "@type" : { + "const" : "ByteArrayDeleteModification" + } + }, + "required" : [ "count", "startPosition", "@type" ] + }, + "ByteArrayDuplicateModification" : { + "type" : "object", + "properties" : { + "@type" : { + "const" : "ByteArrayDuplicateModification" + } + }, + "required" : [ "@type" ] + }, + "ByteArrayExplicitValueModification" : { + "type" : "object", + "properties" : { + "explicitValue" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "@type" : { + "const" : "ByteArrayExplicitValueModification" + } + }, + "required" : [ "explicitValue", "@type" ] + }, + "ByteArrayInsertValueModification" : { + "type" : "object", + "properties" : { + "bytesToInsert" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "startPosition" : { + "type" : "integer" + }, + "@type" : { + "const" : "ByteArrayInsertValueModification" + } + }, + "required" : [ "bytesToInsert", "startPosition", "@type" ] + }, + "ByteArrayPrependValueModification" : { + "type" : "object", + "properties" : { + "bytesToPrepend" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "@type" : { + "const" : "ByteArrayPrependValueModification" + } + }, + "required" : [ "bytesToPrepend", "@type" ] + }, + "ByteArrayShuffleModification" : { + "type" : "object", + "properties" : { + "shuffle" : { + "type" : "array", + "items" : { + "type" : "integer" + } + }, + "@type" : { + "const" : "ByteArrayShuffleModification" + } + }, + "required" : [ "shuffle", "@type" ] + }, + "ByteArrayXorModification" : { + "type" : "object", + "properties" : { + "startPosition" : { + "type" : "integer" + }, + "xor" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "@type" : { + "const" : "ByteArrayXorModification" + } + }, + "required" : [ "startPosition", "xor", "@type" ] + }, + "ByteExplicitValueModification" : { + "type" : "object", + "properties" : { + "explicitValue" : { + "type" : "string" + }, + "@type" : { + "const" : "ByteExplicitValueModification" + } + }, + "required" : [ "explicitValue", "@type" ] + }, + "ByteSubtractModification" : { + "type" : "object", + "properties" : { + "subtrahend" : { + "type" : "string" + }, + "@type" : { + "const" : "ByteSubtractModification" + } + }, + "required" : [ "subtrahend", "@type" ] + }, + "ByteXorModification" : { + "type" : "object", + "properties" : { + "xor" : { + "type" : "string" + }, + "@type" : { + "const" : "ByteXorModification" + } + }, + "required" : [ "xor", "@type" ] + }, + "IntegerAddModification" : { + "type" : "object", + "properties" : { + "summand" : { + "type" : "integer" + }, + "@type" : { + "const" : "IntegerAddModification" + } + }, + "required" : [ "summand", "@type" ] + }, + "IntegerExplicitValueModification" : { + "type" : "object", + "properties" : { + "explicitValue" : { + "type" : "integer" + }, + "@type" : { + "const" : "IntegerExplicitValueModification" + } + }, + "required" : [ "explicitValue", "@type" ] + }, + "IntegerMultiplyModification" : { + "type" : "object", + "properties" : { + "factor" : { + "type" : "integer" + }, + "@type" : { + "const" : "IntegerMultiplyModification" + } + }, + "required" : [ "factor", "@type" ] + }, + "IntegerShiftLeftModification" : { + "type" : "object", + "properties" : { + "shift" : { + "type" : "integer" + }, + "@type" : { + "const" : "IntegerShiftLeftModification" + } + }, + "required" : [ "shift", "@type" ] + }, + "IntegerShiftRightModification" : { + "type" : "object", + "properties" : { + "shift" : { + "type" : "integer" + }, + "@type" : { + "const" : "IntegerShiftRightModification" + } + }, + "required" : [ "shift", "@type" ] + }, + "IntegerSubtractModification" : { + "type" : "object", + "properties" : { + "subtrahend" : { + "type" : "integer" + }, + "@type" : { + "const" : "IntegerSubtractModification" + } + }, + "required" : [ "subtrahend", "@type" ] + }, + "IntegerSwapEndianModification" : { + "type" : "object", + "properties" : { + "@type" : { + "const" : "IntegerSwapEndianModification" + } + }, + "required" : [ "@type" ] + }, + "IntegerXorModification" : { + "type" : "object", + "properties" : { + "xor" : { + "type" : "integer" + }, + "@type" : { + "const" : "IntegerXorModification" + } + }, + "required" : [ "xor", "@type" ] + }, + "LongAddModification" : { + "type" : "object", + "properties" : { + "summand" : { + "type" : "integer" + }, + "@type" : { + "const" : "LongAddModification" + } + }, + "required" : [ "summand", "@type" ] + }, + "LongExplicitValueModification" : { + "type" : "object", + "properties" : { + "explicitValue" : { + "type" : "integer" + }, + "@type" : { + "const" : "LongExplicitValueModification" + } + }, + "required" : [ "explicitValue", "@type" ] + }, + "LongMultiplyModification" : { + "type" : "object", + "properties" : { + "factor" : { + "type" : "integer" + }, + "@type" : { + "const" : "LongMultiplyModification" + } + }, + "required" : [ "factor", "@type" ] + }, + "LongShiftLeftModification" : { + "type" : "object", + "properties" : { + "shift" : { + "type" : "integer" + }, + "@type" : { + "const" : "LongShiftLeftModification" + } + }, + "required" : [ "shift", "@type" ] + }, + "LongShiftRightModification" : { + "type" : "object", + "properties" : { + "shift" : { + "type" : "integer" + }, + "@type" : { + "const" : "LongShiftRightModification" + } + }, + "required" : [ "shift", "@type" ] + }, + "LongSubtractModification" : { + "type" : "object", + "properties" : { + "subtrahend" : { + "type" : "integer" + }, + "@type" : { + "const" : "LongSubtractModification" + } + }, + "required" : [ "subtrahend", "@type" ] + }, + "LongSwapEndianModification" : { + "type" : "object", + "properties" : { + "@type" : { + "const" : "LongSwapEndianModification" + } + }, + "required" : [ "@type" ] + }, + "LongXorModification" : { + "type" : "object", + "properties" : { + "xor" : { + "type" : "integer" + }, + "@type" : { + "const" : "LongXorModification" + } + }, + "required" : [ "xor", "@type" ] + }, + "ModifiableBigInteger" : { + "type" : "object", + "properties" : { + "assertEquals" : { + "type" : "integer" + }, + "modifications" : { + "type" : "array", + "items" : { + "anyOf" : [ { + "$ref" : "#/$defs/BigIntegerAddModification" + }, { + "$ref" : "#/$defs/BigIntegerExplicitValueModification" + }, { + "$ref" : "#/$defs/BigIntegerMultiplyModification" + }, { + "$ref" : "#/$defs/BigIntegerShiftLeftModification" + }, { + "$ref" : "#/$defs/BigIntegerShiftRightModification" + }, { + "$ref" : "#/$defs/BigIntegerSubtractModification" + }, { + "$ref" : "#/$defs/BigIntegerXorModification" + } ] + } + }, + "originalValue" : { + "type" : "integer" + }, + "@type" : { + "const" : "ModifiableBigInteger" + } + }, + "required" : [ "@type" ] + }, + "ModifiableBoolean" : { + "type" : "object", + "properties" : { + "assertEquals" : { + "type" : "boolean" + }, + "modifications" : { + "type" : "array", + "items" : { + "anyOf" : [ { + "$ref" : "#/$defs/BooleanExplicitValueModification" + }, { + "$ref" : "#/$defs/BooleanToggleModification" + } ] + } + }, + "originalValue" : { + "type" : "boolean" + }, + "@type" : { + "const" : "ModifiableBoolean" + } + }, + "required" : [ "@type" ] + }, + "ModifiableByte" : { + "type" : "object", + "properties" : { + "assertEquals" : { + "type" : "string" + }, + "modifications" : { + "type" : "array", + "items" : { + "anyOf" : [ { + "$ref" : "#/$defs/ByteAddModification" + }, { + "$ref" : "#/$defs/ByteExplicitValueModification" + }, { + "$ref" : "#/$defs/ByteSubtractModification" + }, { + "$ref" : "#/$defs/ByteXorModification" + } ] + } + }, + "originalValue" : { + "type" : "string" + }, + "@type" : { + "const" : "ModifiableByte" + } + }, + "required" : [ "@type" ] + }, + "ModifiableByteArray" : { + "type" : "object", + "properties" : { + "assertEquals" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "modifications" : { + "type" : "array", + "items" : { + "anyOf" : [ { + "$ref" : "#/$defs/ByteArrayAppendValueModification" + }, { + "$ref" : "#/$defs/ByteArrayDeleteModification" + }, { + "$ref" : "#/$defs/ByteArrayDuplicateModification" + }, { + "$ref" : "#/$defs/ByteArrayExplicitValueModification" + }, { + "$ref" : "#/$defs/ByteArrayInsertValueModification" + }, { + "$ref" : "#/$defs/ByteArrayPrependValueModification" + }, { + "$ref" : "#/$defs/ByteArrayShuffleModification" + }, { + "$ref" : "#/$defs/ByteArrayXorModification" + } ] + } + }, + "originalValue" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "@type" : { + "const" : "ModifiableByteArray" + } + }, + "required" : [ "@type" ] + }, + "ModifiableInteger" : { + "type" : "object", + "properties" : { + "assertEquals" : { + "type" : "integer" + }, + "modifications" : { + "type" : "array", + "items" : { + "anyOf" : [ { + "$ref" : "#/$defs/IntegerAddModification" + }, { + "$ref" : "#/$defs/IntegerExplicitValueModification" + }, { + "$ref" : "#/$defs/IntegerMultiplyModification" + }, { + "$ref" : "#/$defs/IntegerShiftLeftModification" + }, { + "$ref" : "#/$defs/IntegerShiftRightModification" + }, { + "$ref" : "#/$defs/IntegerSubtractModification" + }, { + "$ref" : "#/$defs/IntegerSwapEndianModification" + }, { + "$ref" : "#/$defs/IntegerXorModification" + } ] + } + }, + "originalValue" : { + "type" : "integer" + }, + "@type" : { + "const" : "ModifiableInteger" + } + }, + "required" : [ "@type" ] + }, + "ModifiableLengthField" : { + "type" : "object", + "properties" : { + "assertEquals" : { + "type" : "integer" + }, + "modifications" : { + "type" : "array", + "items" : { + "anyOf" : [ { + "$ref" : "#/$defs/IntegerAddModification" + }, { + "$ref" : "#/$defs/IntegerExplicitValueModification" + }, { + "$ref" : "#/$defs/IntegerMultiplyModification" + }, { + "$ref" : "#/$defs/IntegerShiftLeftModification" + }, { + "$ref" : "#/$defs/IntegerShiftRightModification" + }, { + "$ref" : "#/$defs/IntegerSubtractModification" + }, { + "$ref" : "#/$defs/IntegerSwapEndianModification" + }, { + "$ref" : "#/$defs/IntegerXorModification" + } ] + } + }, + "originalValue" : { + "type" : "integer" + }, + "ref" : { + "$ref" : "#/$defs/ModifiableByteArray" + }, + "@type" : { + "const" : "ModifiableLengthField" + } + }, + "required" : [ "ref", "@type" ] + }, + "ModifiableLong" : { + "type" : "object", + "properties" : { + "assertEquals" : { + "type" : "integer" + }, + "modifications" : { + "type" : "array", + "items" : { + "anyOf" : [ { + "$ref" : "#/$defs/LongAddModification" + }, { + "$ref" : "#/$defs/LongExplicitValueModification" + }, { + "$ref" : "#/$defs/LongMultiplyModification" + }, { + "$ref" : "#/$defs/LongShiftLeftModification" + }, { + "$ref" : "#/$defs/LongShiftRightModification" + }, { + "$ref" : "#/$defs/LongSubtractModification" + }, { + "$ref" : "#/$defs/LongSwapEndianModification" + }, { + "$ref" : "#/$defs/LongXorModification" + } ] + } + }, + "originalValue" : { + "type" : "integer" + }, + "@type" : { + "const" : "ModifiableLong" + } + }, + "required" : [ "@type" ] + }, + "ModifiableString" : { + "type" : "object", + "properties" : { + "assertEquals" : { + "type" : "string" + }, + "modifications" : { + "type" : "array", + "items" : { + "anyOf" : [ { + "$ref" : "#/$defs/StringAppendValueModification" + }, { + "$ref" : "#/$defs/StringDeleteModification" + }, { + "$ref" : "#/$defs/StringExplicitValueModification" + }, { + "$ref" : "#/$defs/StringInsertValueModification" + }, { + "$ref" : "#/$defs/StringPrependValueModification" + } ] + } + }, + "originalValue" : { + "type" : "string" + }, + "@type" : { + "const" : "ModifiableString" + } + }, + "required" : [ "@type" ] + }, + "StringAppendValueModification" : { + "type" : "object", + "properties" : { + "appendValue" : { + "type" : "string" + }, + "@type" : { + "const" : "StringAppendValueModification" + } + }, + "required" : [ "appendValue", "@type" ] + }, + "StringDeleteModification" : { + "type" : "object", + "properties" : { + "count" : { + "type" : "integer" + }, + "startPosition" : { + "type" : "integer" + }, + "@type" : { + "const" : "StringDeleteModification" + } + }, + "required" : [ "count", "startPosition", "@type" ] + }, + "StringExplicitValueModification" : { + "type" : "object", + "properties" : { + "explicitValue" : { + "type" : "string" + }, + "@type" : { + "const" : "StringExplicitValueModification" + } + }, + "required" : [ "explicitValue", "@type" ] + }, + "StringInsertValueModification" : { + "type" : "object", + "properties" : { + "insertValue" : { + "type" : "string" + }, + "startPosition" : { + "type" : "integer" + }, + "@type" : { + "const" : "StringInsertValueModification" + } + }, + "required" : [ "insertValue", "startPosition", "@type" ] + }, + "StringPrependValueModification" : { + "type" : "object", + "properties" : { + "prependValue" : { + "type" : "string" + }, + "@type" : { + "const" : "StringPrependValueModification" + } + }, + "required" : [ "prependValue", "@type" ] + } + }, + "anyOf" : [ { + "$ref" : "#/$defs/ModifiableBigInteger" + }, { + "$ref" : "#/$defs/ModifiableBoolean" + }, { + "$ref" : "#/$defs/ModifiableByteArray" + }, { + "$ref" : "#/$defs/ModifiableInteger" + }, { + "$ref" : "#/$defs/ModifiableLengthField" + }, { + "$ref" : "#/$defs/ModifiableLong" + }, { + "$ref" : "#/$defs/ModifiableByte" + }, { + "$ref" : "#/$defs/ModifiableString" + } ] +} \ No newline at end of file diff --git a/src/test/java/de/rub/nds/modifiablevariable/serialization/BigIntegerSerializationTest.java b/src/test/java/de/rub/nds/modifiablevariable/json/BigIntegerSerializationTest.java similarity index 53% rename from src/test/java/de/rub/nds/modifiablevariable/serialization/BigIntegerSerializationTest.java rename to src/test/java/de/rub/nds/modifiablevariable/json/BigIntegerSerializationTest.java index 206da1e4..c3fc4838 100644 --- a/src/test/java/de/rub/nds/modifiablevariable/serialization/BigIntegerSerializationTest.java +++ b/src/test/java/de/rub/nds/modifiablevariable/json/BigIntegerSerializationTest.java @@ -5,71 +5,52 @@ * * Licensed under Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 */ -package de.rub.nds.modifiablevariable.serialization; +package de.rub.nds.modifiablevariable.json; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotSame; +import com.fasterxml.jackson.databind.ObjectMapper; import de.rub.nds.modifiablevariable.VariableModification; import de.rub.nds.modifiablevariable.biginteger.BigIntegerAddModification; import de.rub.nds.modifiablevariable.biginteger.ModifiableBigInteger; -import de.rub.nds.modifiablevariable.bytearray.ByteArrayExplicitValueModification; -import jakarta.xml.bind.JAXBContext; -import jakarta.xml.bind.JAXBException; -import jakarta.xml.bind.Marshaller; -import jakarta.xml.bind.Unmarshaller; -import java.io.StringReader; -import java.io.StringWriter; import java.math.BigInteger; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class BigIntegerSerializationTest { - private static final Logger LOGGER = LogManager.getLogger(BigIntegerSerializationTest.class); + private static final Logger LOGGER = LogManager.getLogger(); + private static ObjectMapper mapper; private ModifiableBigInteger start; - private BigInteger expectedResult, result; - private StringWriter writer; - - private JAXBContext context; - - private Marshaller m; - - private Unmarshaller um; + @BeforeAll + static void setUpClass() { + mapper = new ObjectMapper(); + mapper.registerModule(new ModifiableVariableModule()); + mapper.setVisibility(ModifiableVariableModule.getFieldVisibilityChecker()); + } @BeforeEach - void setUp() throws JAXBException { + void setUp() { start = new ModifiableBigInteger(); start.setOriginalValue(BigInteger.TEN); expectedResult = null; result = null; - - writer = new StringWriter(); - context = - JAXBContext.newInstance( - ModifiableBigInteger.class, - BigIntegerAddModification.class, - ByteArrayExplicitValueModification.class); - m = context.createMarshaller(); - m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - um = context.createUnmarshaller(); } @Test void testSerializeDeserializeSimple() throws Exception { start.clearModifications(); - m.marshal(start, writer); - String xmlString = writer.toString(); - LOGGER.info(xmlString); - - um = context.createUnmarshaller(); - ModifiableBigInteger mv = (ModifiableBigInteger) um.unmarshal(new StringReader(xmlString)); + String jsonString = mapper.writeValueAsString(start); + LOGGER.debug(jsonString); + ModifiableBigInteger mv = mapper.readValue(jsonString, ModifiableBigInteger.class); expectedResult = new BigInteger("10"); result = mv.getValue(); @@ -81,13 +62,10 @@ void testSerializeDeserializeSimple() throws Exception { void testSerializeDeserializeWithModification() throws Exception { VariableModification modifier = new BigIntegerAddModification(BigInteger.ONE); start.setModifications(modifier); - m.marshal(start, writer); - - String xmlString = writer.toString(); - LOGGER.debug(xmlString); - um = context.createUnmarshaller(); - ModifiableBigInteger mv = (ModifiableBigInteger) um.unmarshal(new StringReader(xmlString)); + String jsonString = mapper.writeValueAsString(start); + LOGGER.debug(jsonString); + ModifiableBigInteger mv = mapper.readValue(jsonString, ModifiableBigInteger.class); expectedResult = new BigInteger("11"); result = mv.getValue(); diff --git a/src/test/java/de/rub/nds/modifiablevariable/serialization/ByteArraySerializationTest.java b/src/test/java/de/rub/nds/modifiablevariable/json/ByteArraySerializationTest.java similarity index 53% rename from src/test/java/de/rub/nds/modifiablevariable/serialization/ByteArraySerializationTest.java rename to src/test/java/de/rub/nds/modifiablevariable/json/ByteArraySerializationTest.java index fa827c41..6e4506c9 100644 --- a/src/test/java/de/rub/nds/modifiablevariable/serialization/ByteArraySerializationTest.java +++ b/src/test/java/de/rub/nds/modifiablevariable/json/ByteArraySerializationTest.java @@ -5,71 +5,51 @@ * * Licensed under Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 */ -package de.rub.nds.modifiablevariable.serialization; +package de.rub.nds.modifiablevariable.json; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertNotSame; +import com.fasterxml.jackson.databind.ObjectMapper; import de.rub.nds.modifiablevariable.VariableModification; import de.rub.nds.modifiablevariable.bytearray.*; -import jakarta.xml.bind.JAXBContext; -import jakarta.xml.bind.JAXBException; -import jakarta.xml.bind.Marshaller; -import jakarta.xml.bind.Unmarshaller; -import java.io.StringReader; -import java.io.StringWriter; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class ByteArraySerializationTest { - private static final Logger LOGGER = LogManager.getLogger(ByteArraySerializationTest.class); + private static final Logger LOGGER = LogManager.getLogger(); + private static ObjectMapper mapper; private ModifiableByteArray start; - private byte[] expectedResult, result; - private StringWriter writer; - - private JAXBContext context; - - private Marshaller m; - - private Unmarshaller um; + @BeforeAll + static void setUpClass() { + mapper = new ObjectMapper(); + mapper.registerModule(new ModifiableVariableModule()); + mapper.setVisibility(ModifiableVariableModule.getFieldVisibilityChecker()); + } @BeforeEach - void setUp() throws JAXBException { + void setUp() { start = new ModifiableByteArray(); start.setOriginalValue(new byte[] {(byte) 0xff, 1, 2, 3}); expectedResult = null; result = null; - - writer = new StringWriter(); - context = - JAXBContext.newInstance( - ModifiableByteArray.class, - ByteArrayDeleteModification.class, - ByteArrayExplicitValueModification.class, - ByteArrayInsertValueModification.class, - ByteArrayXorModification.class); - m = context.createMarshaller(); - m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - um = context.createUnmarshaller(); } @Test void testSerializeDeserializeSimple() throws Exception { start.clearModifications(); start.setAssertEquals(new byte[] {(byte) 0xff, 5, 44, 3}); - m.marshal(start, writer); - String xmlString = writer.toString(); - LOGGER.debug(xmlString); - - um = context.createUnmarshaller(); - ModifiableByteArray mba = (ModifiableByteArray) um.unmarshal(new StringReader(xmlString)); + String jsonString = mapper.writeValueAsString(start); + LOGGER.debug(jsonString); + ModifiableByteArray mba = mapper.readValue(jsonString, ModifiableByteArray.class); expectedResult = new byte[] {(byte) 0xff, 1, 2, 3}; result = mba.getValue(); @@ -82,13 +62,10 @@ void testSerializeDeserializeWithModification() throws Exception { VariableModification modifier = new ByteArrayInsertValueModification(new byte[] {1, 2}, 0); start.setModifications(modifier); - m.marshal(start, writer); - - String xmlString = writer.toString(); - LOGGER.debug(xmlString); - um = context.createUnmarshaller(); - ModifiableByteArray mba = (ModifiableByteArray) um.unmarshal(new StringReader(xmlString)); + String jsonString = mapper.writeValueAsString(start); + LOGGER.debug(jsonString); + ModifiableByteArray mba = mapper.readValue(jsonString, ModifiableByteArray.class); expectedResult = new byte[] {1, 2, (byte) 0xff, 1, 2, 3}; result = mba.getValue(); diff --git a/src/test/java/de/rub/nds/modifiablevariable/serialization/ByteSerializationTest.java b/src/test/java/de/rub/nds/modifiablevariable/json/ByteSerializationTest.java similarity index 62% rename from src/test/java/de/rub/nds/modifiablevariable/serialization/ByteSerializationTest.java rename to src/test/java/de/rub/nds/modifiablevariable/json/ByteSerializationTest.java index cd4c3c74..c03251f3 100644 --- a/src/test/java/de/rub/nds/modifiablevariable/serialization/ByteSerializationTest.java +++ b/src/test/java/de/rub/nds/modifiablevariable/json/ByteSerializationTest.java @@ -5,56 +5,46 @@ * * Licensed under Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 */ -package de.rub.nds.modifiablevariable.serialization; +package de.rub.nds.modifiablevariable.json; import static org.junit.jupiter.api.Assertions.*; +import com.fasterxml.jackson.databind.ObjectMapper; import de.rub.nds.modifiablevariable.singlebyte.*; -import jakarta.xml.bind.JAXBContext; -import jakarta.xml.bind.JAXBException; -import jakarta.xml.bind.Marshaller; -import jakarta.xml.bind.Unmarshaller; -import java.io.StringReader; -import java.io.StringWriter; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class ByteSerializationTest { + private static final Logger LOGGER = LogManager.getLogger(); + private static ObjectMapper mapper; + private ModifiableByte start; private Byte expectedResult, result; - private StringWriter writer; - private JAXBContext context; - private Marshaller m; - private Unmarshaller um; + + @BeforeAll + public static void setUpClass() { + mapper = new ObjectMapper(); + mapper.registerModule(new ModifiableVariableModule()); + mapper.setVisibility(ModifiableVariableModule.getFieldVisibilityChecker()); + } @BeforeEach - void setUp() throws JAXBException { + void setUp() { start = new ModifiableByte(); start.setOriginalValue((byte) 10); - - writer = new StringWriter(); - context = - JAXBContext.newInstance( - ModifiableByte.class, - ByteAddModification.class, - ByteSubtractModification.class, - ByteXorModification.class, - ByteExplicitValueModification.class); - m = context.createMarshaller(); - m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - um = context.createUnmarshaller(); } @Test void testSerializeDeserializeSimple() throws Exception { start.clearModifications(); - m.marshal(start, writer); - String xmlString = writer.toString(); - - um = context.createUnmarshaller(); - ModifiableByte mv = (ModifiableByte) um.unmarshal(new StringReader(xmlString)); + String jsonString = mapper.writeValueAsString(start); + LOGGER.debug(jsonString); + ModifiableByte mv = mapper.readValue(jsonString, ModifiableByte.class); expectedResult = 10; result = mv.getValue(); @@ -67,10 +57,9 @@ void testSerializeDeserializeWithAddModification() throws Exception { ByteAddModification mod = new ByteAddModification((byte) 5); start.setModifications(mod); - m.marshal(start, writer); - String xmlString = writer.toString(); - - ModifiableByte mv = (ModifiableByte) um.unmarshal(new StringReader(xmlString)); + String jsonString = mapper.writeValueAsString(start); + LOGGER.debug(jsonString); + ModifiableByte mv = mapper.readValue(jsonString, ModifiableByte.class); expectedResult = 15; // 10 + 5 result = mv.getValue(); @@ -84,10 +73,9 @@ void testSerializeDeserializeWithSubtractModification() throws Exception { ByteSubtractModification mod = new ByteSubtractModification((byte) 3); start.setModifications(mod); - m.marshal(start, writer); - String xmlString = writer.toString(); - - ModifiableByte mv = (ModifiableByte) um.unmarshal(new StringReader(xmlString)); + String jsonString = mapper.writeValueAsString(start); + LOGGER.debug(jsonString); + ModifiableByte mv = mapper.readValue(jsonString, ModifiableByte.class); expectedResult = 7; // 10 - 3 result = mv.getValue(); @@ -101,10 +89,9 @@ void testSerializeDeserializeWithXorModification() throws Exception { ByteXorModification mod = new ByteXorModification((byte) 6); start.setModifications(mod); - m.marshal(start, writer); - String xmlString = writer.toString(); - - ModifiableByte mv = (ModifiableByte) um.unmarshal(new StringReader(xmlString)); + String jsonString = mapper.writeValueAsString(start); + LOGGER.debug(jsonString); + ModifiableByte mv = mapper.readValue(jsonString, ModifiableByte.class); expectedResult = (byte) (10 ^ 6); result = mv.getValue(); @@ -118,10 +105,9 @@ void testSerializeDeserializeWithExplicitValueModification() throws Exception { ByteExplicitValueModification mod = new ByteExplicitValueModification((byte) 42); start.setModifications(mod); - m.marshal(start, writer); - String xmlString = writer.toString(); - - ModifiableByte mv = (ModifiableByte) um.unmarshal(new StringReader(xmlString)); + String jsonString = mapper.writeValueAsString(start); + LOGGER.debug(jsonString); + ModifiableByte mv = mapper.readValue(jsonString, ModifiableByte.class); expectedResult = 42; result = mv.getValue(); @@ -138,10 +124,9 @@ void testSerializeDeserializeWithDoubleModification() throws Exception { start.setModifications(addMod, xorMod); - m.marshal(start, writer); - String xmlString = writer.toString(); - - ModifiableByte mv = (ModifiableByte) um.unmarshal(new StringReader(xmlString)); + String jsonString = mapper.writeValueAsString(start); + LOGGER.debug(jsonString); + ModifiableByte mv = mapper.readValue(jsonString, ModifiableByte.class); // Expected: (10 + 5) ^ 3 = 15 ^ 3 = 12 expectedResult = (byte) ((10 + 5) ^ 3); @@ -150,8 +135,7 @@ void testSerializeDeserializeWithDoubleModification() throws Exception { assertEquals(start.getOriginalValue(), mv.getOriginalValue()); // Verify the XML content, but don't be too strict about exact representation - assertTrue( - xmlString.contains("ByteAddModification") || xmlString.contains("modifications")); + assertTrue(jsonString.contains("ByteAdd") || jsonString.contains("modifications")); // The actual serialization of multiple modifications might vary, so focus on correctness } @@ -159,10 +143,9 @@ void testSerializeDeserializeWithDoubleModification() throws Exception { void testSerializeDeserializeWithNullValue() throws Exception { ModifiableByte nullByte = new ModifiableByte(); - m.marshal(nullByte, writer); - String xmlString = writer.toString(); - - ModifiableByte mv = (ModifiableByte) um.unmarshal(new StringReader(xmlString)); + String jsonString = mapper.writeValueAsString(nullByte); + LOGGER.debug(jsonString); + ModifiableByte mv = mapper.readValue(jsonString, ModifiableByte.class); assertNull(mv.getOriginalValue()); assertNull(mv.getValue()); @@ -173,10 +156,9 @@ void testSerializeDeserializeWithAssertions() throws Exception { start.setAssertEquals((byte) 15); start.setModifications(new ByteAddModification((byte) 5)); - m.marshal(start, writer); - String xmlString = writer.toString(); - - ModifiableByte mv = (ModifiableByte) um.unmarshal(new StringReader(xmlString)); + String jsonString = mapper.writeValueAsString(start); + LOGGER.debug(jsonString); + ModifiableByte mv = mapper.readValue(jsonString, ModifiableByte.class); assertEquals(start.getAssertEquals(), mv.getAssertEquals()); assertEquals((byte) 15, mv.getValue()); @@ -188,22 +170,19 @@ void testSerializeDeserializeWithMinMaxValues() throws Exception { // Test with MIN_VALUE start.setOriginalValue(Byte.MIN_VALUE); - m.marshal(start, writer); - String xmlString = writer.toString(); - - ModifiableByte mv = (ModifiableByte) um.unmarshal(new StringReader(xmlString)); + String jsonString = mapper.writeValueAsString(start); + LOGGER.debug(jsonString); + ModifiableByte mv = mapper.readValue(jsonString, ModifiableByte.class); assertEquals(Byte.MIN_VALUE, mv.getOriginalValue()); assertEquals(Byte.MIN_VALUE, mv.getValue()); // Test with MAX_VALUE start.setOriginalValue(Byte.MAX_VALUE); - writer = new StringWriter(); - - m.marshal(start, writer); - xmlString = writer.toString(); - mv = (ModifiableByte) um.unmarshal(new StringReader(xmlString)); + jsonString = mapper.writeValueAsString(start); + LOGGER.debug(jsonString); + mv = mapper.readValue(jsonString, ModifiableByte.class); assertEquals(Byte.MAX_VALUE, mv.getOriginalValue()); assertEquals(Byte.MAX_VALUE, mv.getValue()); @@ -219,17 +198,12 @@ void testCopyConstructorSerializationConsistency() throws Exception { ModifiableByte copy = new ModifiableByte(start); // Serialize both - m.marshal(start, writer); - String originalXml = writer.toString(); - - writer = new StringWriter(); - m.marshal(copy, writer); - String copyXml = writer.toString(); + String originalJson = mapper.writeValueAsString(start); + String copyJson = mapper.writeValueAsString(copy); // Deserialize both XMLs - ModifiableByte deserializedOriginal = - (ModifiableByte) um.unmarshal(new StringReader(originalXml)); - ModifiableByte deserializedCopy = (ModifiableByte) um.unmarshal(new StringReader(copyXml)); + ModifiableByte deserializedOriginal = mapper.readValue(originalJson, ModifiableByte.class); + ModifiableByte deserializedCopy = mapper.readValue(copyJson, ModifiableByte.class); // Both deserialized objects should be equal assertEquals(deserializedOriginal.getOriginalValue(), deserializedCopy.getOriginalValue()); diff --git a/src/test/java/de/rub/nds/modifiablevariable/json/LongSerializationTest.java b/src/test/java/de/rub/nds/modifiablevariable/json/LongSerializationTest.java new file mode 100644 index 00000000..c4a8c029 --- /dev/null +++ b/src/test/java/de/rub/nds/modifiablevariable/json/LongSerializationTest.java @@ -0,0 +1,71 @@ +/* + * ModifiableVariable - A Variable Concept for Runtime Modifications + * + * Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH + * + * Licensed under Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 + */ +package de.rub.nds.modifiablevariable.json; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fasterxml.jackson.databind.ObjectMapper; +import de.rub.nds.modifiablevariable.VariableModification; +import de.rub.nds.modifiablevariable.longint.LongAddModification; +import de.rub.nds.modifiablevariable.longint.ModifiableLong; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class LongSerializationTest { + + private static final Logger LOGGER = LogManager.getLogger(); + private static ObjectMapper mapper; + + private ModifiableLong start; + private Long expectedResult, result; + + @BeforeAll + public static void setUpClass() { + mapper = new ObjectMapper(); + mapper.registerModule(new ModifiableVariableModule()); + mapper.setVisibility(ModifiableVariableModule.getFieldVisibilityChecker()); + } + + @BeforeEach + void setUp() { + start = new ModifiableLong(); + start.setOriginalValue(10L); + expectedResult = null; + result = null; + } + + @Test + void testSerializeDeserializeSimple() throws Exception { + start.clearModifications(); + + String jsonString = mapper.writeValueAsString(start); + LOGGER.debug(jsonString); + ModifiableLong mv = mapper.readValue(jsonString, ModifiableLong.class); + + expectedResult = 10L; + result = mv.getValue(); + assertEquals(expectedResult, result); + } + + @Test + void testSerializeDeserializeWithModification() throws Exception { + VariableModification modifier = new LongAddModification(1L); + start.setModifications(modifier); + + String jsonString = mapper.writeValueAsString(start); + LOGGER.debug(jsonString); + ModifiableLong mv = mapper.readValue(jsonString, ModifiableLong.class); + + expectedResult = 11L; + result = mv.getValue(); + assertEquals(expectedResult, result); + } +} diff --git a/src/test/java/de/rub/nds/modifiablevariable/serialization/StringSerializationTest.java b/src/test/java/de/rub/nds/modifiablevariable/json/StringSerializationTest.java similarity index 57% rename from src/test/java/de/rub/nds/modifiablevariable/serialization/StringSerializationTest.java rename to src/test/java/de/rub/nds/modifiablevariable/json/StringSerializationTest.java index d998136e..adc08339 100644 --- a/src/test/java/de/rub/nds/modifiablevariable/serialization/StringSerializationTest.java +++ b/src/test/java/de/rub/nds/modifiablevariable/json/StringSerializationTest.java @@ -5,67 +5,50 @@ * * Licensed under Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 */ -package de.rub.nds.modifiablevariable.serialization; +package de.rub.nds.modifiablevariable.json; import static org.junit.jupiter.api.Assertions.*; +import com.fasterxml.jackson.databind.ObjectMapper; import de.rub.nds.modifiablevariable.VariableModification; import de.rub.nds.modifiablevariable.string.ModifiableString; import de.rub.nds.modifiablevariable.string.StringInsertValueModification; -import jakarta.xml.bind.JAXBContext; -import jakarta.xml.bind.JAXBException; -import jakarta.xml.bind.Marshaller; -import jakarta.xml.bind.Unmarshaller; -import java.io.StringReader; -import java.io.StringWriter; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class StringSerializationTest { private static final Logger LOGGER = LogManager.getLogger(); + private static ObjectMapper mapper; private ModifiableString start; - private String expectedResult, result; - private StringWriter writer; - - private JAXBContext context; - - private Marshaller m; - - private Unmarshaller um; + @BeforeAll + public static void setUpClass() { + mapper = new ObjectMapper(); + mapper.registerModule(new ModifiableVariableModule()); + mapper.setVisibility(ModifiableVariableModule.getFieldVisibilityChecker()); + } @BeforeEach - void setUp() throws JAXBException { + void setUp() { start = new ModifiableString("Hello from Test ❤️\\ \u0000 \u0001 \u0006"); expectedResult = null; result = null; - - writer = new StringWriter(); - context = - JAXBContext.newInstance( - ModifiableString.class, StringInsertValueModification.class); - m = context.createMarshaller(); - m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - um = context.createUnmarshaller(); } @Test void testSerializeDeserializeSimple() throws Exception { start.clearModifications(); start.setAssertEquals("Hello from Test 2 \\ \u0000 \u0001 \u0006"); - m.marshal(start, writer); - String xmlString = writer.toString(); - LOGGER.debug(xmlString); - - um = context.createUnmarshaller(); - ModifiableString unmarshalled = - (ModifiableString) um.unmarshal(new StringReader(xmlString)); + String jsonString = mapper.writeValueAsString(start); + LOGGER.debug(jsonString); + ModifiableString unmarshalled = mapper.readValue(jsonString, ModifiableString.class); expectedResult = "Hello from Test ❤️\\ \u0000 \u0001 \u0006"; result = unmarshalled.getValue(); @@ -76,14 +59,10 @@ void testSerializeDeserializeSimple() throws Exception { void testSerializeDeserializeWithModification() throws Exception { VariableModification modifier = new StringInsertValueModification("Uff! ", 0); start.setModifications(modifier); - m.marshal(start, writer); - - String xmlString = writer.toString(); - LOGGER.debug(xmlString); - um = context.createUnmarshaller(); - ModifiableString unmarshalled = - (ModifiableString) um.unmarshal(new StringReader(xmlString)); + String jsonString = mapper.writeValueAsString(start); + LOGGER.debug(jsonString); + ModifiableString unmarshalled = mapper.readValue(jsonString, ModifiableString.class); expectedResult = "Uff! Hello from Test ❤️\\ \u0000 \u0001 \u0006"; result = unmarshalled.getValue(); diff --git a/src/test/java/de/rub/nds/modifiablevariable/serialization/LongSerializationTest.java b/src/test/java/de/rub/nds/modifiablevariable/serialization/LongSerializationTest.java deleted file mode 100644 index 3facacbc..00000000 --- a/src/test/java/de/rub/nds/modifiablevariable/serialization/LongSerializationTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * ModifiableVariable - A Variable Concept for Runtime Modifications - * - * Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH - * - * Licensed under Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 - */ -package de.rub.nds.modifiablevariable.serialization; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import de.rub.nds.modifiablevariable.VariableModification; -import de.rub.nds.modifiablevariable.bytearray.ByteArrayExplicitValueModification; -import de.rub.nds.modifiablevariable.longint.LongAddModification; -import de.rub.nds.modifiablevariable.longint.ModifiableLong; -import jakarta.xml.bind.JAXBContext; -import jakarta.xml.bind.JAXBException; -import jakarta.xml.bind.Marshaller; -import jakarta.xml.bind.Unmarshaller; -import java.io.StringReader; -import java.io.StringWriter; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -class LongSerializationTest { - - private static final Logger LOGGER = LogManager.getLogger(LongSerializationTest.class); - - private ModifiableLong start; - - private Long expectedResult, result; - - private StringWriter writer; - - private JAXBContext context; - - private Marshaller m; - - private Unmarshaller um; - - @BeforeEach - void setUp() throws JAXBException { - start = new ModifiableLong(); - start.setOriginalValue(10L); - expectedResult = null; - result = null; - - writer = new StringWriter(); - context = - JAXBContext.newInstance( - ModifiableLong.class, - LongAddModification.class, - ByteArrayExplicitValueModification.class); - m = context.createMarshaller(); - m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - um = context.createUnmarshaller(); - } - - @Test - void testSerializeDeserializeSimple() throws Exception { - start.clearModifications(); - m.marshal(start, writer); - - String xmlString = writer.toString(); - LOGGER.info(xmlString); - - um = context.createUnmarshaller(); - ModifiableLong mv = (ModifiableLong) um.unmarshal(new StringReader(xmlString)); - - expectedResult = 10L; - result = mv.getValue(); - assertEquals(expectedResult, result); - } - - @Test - void testSerializeDeserializeWithModification() throws Exception { - VariableModification modifier = new LongAddModification(1L); - start.setModifications(modifier); - m.marshal(start, writer); - - String xmlString = writer.toString(); - LOGGER.debug(xmlString); - - um = context.createUnmarshaller(); - ModifiableLong mv = (ModifiableLong) um.unmarshal(new StringReader(xmlString)); - - expectedResult = 11L; - result = mv.getValue(); - assertEquals(expectedResult, result); - } -}