Consistently serialize neural network data (#2224)

This commit is contained in:
Gold856
2025-12-07 18:45:14 -05:00
committed by GitHub
parent 192e2a115c
commit 6285f1ee24
3 changed files with 50 additions and 108 deletions

View File

@@ -17,15 +17,9 @@
package org.photonvision.common.util.file;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
@@ -37,7 +31,6 @@ import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jetty.io.EofException;
@@ -45,89 +38,41 @@ import org.eclipse.jetty.io.EofException;
public class JacksonUtils {
public static class UIMap extends HashMap<String, Object> {}
// Custom Path serializer that outputs just the path string without file:/ prefix
public static class PathSerializer extends JsonSerializer<Path> {
@Override
public void serialize(Path value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
if (value == null) {
gen.writeNull();
} else {
gen.writeString(value.toString());
}
}
}
// Custom Path deserializer that reads path strings
public static class PathDeserializer extends JsonDeserializer<Path> {
@Override
public Path deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String pathString = p.getValueAsString();
if (pathString == null || pathString.isEmpty()) {
return null;
}
// Handle case where old serialized data might still have file:/ prefix
if (pathString.startsWith("file:/")) {
pathString = pathString.substring(6); // Remove "file:/" prefix
}
return Paths.get(pathString);
}
}
// Custom Path key deserializer for Maps with Path keys
public static class PathKeyDeserializer extends com.fasterxml.jackson.databind.KeyDeserializer {
@Override
public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException {
if (key == null || key.isEmpty()) {
return null;
}
// Handle case where old serialized data might still have file:/ prefix
if (key.startsWith("file:/")) {
key = key.substring(6); // Remove "file:/" prefix
}
return Paths.get(key);
}
}
// Helper method to create ObjectMapper with Path serialization support
private static ObjectMapper createObjectMapperWithPathSupport(Class<?> baseType) {
PolymorphicTypeValidator ptv =
BasicPolymorphicTypeValidator.builder().allowIfBaseType(baseType).build();
SimpleModule pathModule = new SimpleModule();
pathModule.addSerializer(Path.class, new PathSerializer());
pathModule.addDeserializer(Path.class, new PathDeserializer());
pathModule.addKeyDeserializer(Path.class, new PathKeyDeserializer());
return JsonMapper.builder()
.configure(JsonReadFeature.ALLOW_JAVA_COMMENTS, true)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT)
.addModule(pathModule)
.build();
}
public static <T> void serialize(Path path, T object) throws IOException {
serialize(path, object, true);
}
public static <T> String serializeToString(T object) throws IOException {
ObjectMapper objectMapper = createObjectMapperWithPathSupport(object.getClass());
PolymorphicTypeValidator ptv =
BasicPolymorphicTypeValidator.builder().allowIfBaseType(object.getClass()).build();
ObjectMapper objectMapper =
JsonMapper.builder()
.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT)
.build();
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
}
public static <T> void serialize(Path path, T object, boolean forceSync) throws IOException {
ObjectMapper objectMapper = createObjectMapperWithPathSupport(object.getClass());
PolymorphicTypeValidator ptv =
BasicPolymorphicTypeValidator.builder().allowIfBaseType(object.getClass()).build();
ObjectMapper objectMapper =
JsonMapper.builder()
.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT)
.build();
String json = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
saveJsonString(json, path, forceSync);
}
public static <T> T deserialize(Map<?, ?> s, Class<T> ref) throws IOException {
ObjectMapper objectMapper = createObjectMapperWithPathSupport(ref);
PolymorphicTypeValidator ptv =
BasicPolymorphicTypeValidator.builder().allowIfBaseType(ref).build();
ObjectMapper objectMapper =
JsonMapper.builder()
.configure(JsonReadFeature.ALLOW_JAVA_COMMENTS, true)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT)
.build();
return objectMapper.convertValue(s, ref);
}
@@ -136,14 +81,28 @@ public class JacksonUtils {
throw new EofException("Provided empty string for class " + ref.getName());
}
ObjectMapper objectMapper = createObjectMapperWithPathSupport(ref);
objectMapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);
PolymorphicTypeValidator ptv =
BasicPolymorphicTypeValidator.builder().allowIfBaseType(ref).build();
ObjectMapper objectMapper =
JsonMapper.builder()
.configure(JsonReadFeature.ALLOW_JAVA_COMMENTS, true)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)
.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT)
.build();
return objectMapper.readValue(s, ref);
}
public static <T> T deserialize(Path path, Class<T> ref) throws IOException {
ObjectMapper objectMapper = createObjectMapperWithPathSupport(ref);
PolymorphicTypeValidator ptv =
BasicPolymorphicTypeValidator.builder().allowIfBaseType(ref).build();
ObjectMapper objectMapper =
JsonMapper.builder()
.configure(JsonReadFeature.ALLOW_JAVA_COMMENTS, true)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT)
.build();
File jsonFile = new File(path.toString());
if (jsonFile.exists() && jsonFile.length() > 0) {
return objectMapper.readValue(jsonFile, ref);
@@ -156,12 +115,6 @@ public class JacksonUtils {
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(ref, deserializer);
// Add Path support to custom deserializer case as well
module.addSerializer(Path.class, new PathSerializer());
module.addDeserializer(Path.class, new PathDeserializer());
module.addKeyDeserializer(Path.class, new PathKeyDeserializer());
objectMapper.registerModule(module);
File jsonFile = new File(path.toString());
@@ -182,12 +135,6 @@ public class JacksonUtils {
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(ref, serializer);
// Add Path support to custom serializer case as well
module.addSerializer(Path.class, new PathSerializer());
module.addDeserializer(Path.class, new PathDeserializer());
module.addKeyDeserializer(Path.class, new PathKeyDeserializer());
objectMapper.registerModule(module);
String json = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
saveJsonString(json, path, forceSync);