Skip to content

Commit

Permalink
feat: add better support for serializing records to XML
Browse files Browse the repository at this point in the history
  • Loading branch information
jonas-grgt committed Feb 7, 2024
1 parent 38cea72 commit 3b22c7d
Show file tree
Hide file tree
Showing 28 changed files with 408 additions and 211 deletions.
2 changes: 1 addition & 1 deletion xjx-serdes/src/main/java/io/jonasg/xjx/serdes/Tag.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
* </ul>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.RECORD_COMPONENT})
public @interface Tag {
/**
* Specifies the Path expression indicating the location of the XML data for serialization and deserialization.
Expand Down
14 changes: 7 additions & 7 deletions xjx-serdes/src/main/java/io/jonasg/xjx/serdes/XjxSerdes.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package io.jonasg.xjx.serdes;

import java.io.Reader;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;

import io.jonasg.xjx.sax.SaxParser;
import io.jonasg.xjx.serdes.deserialize.MapOf;
import io.jonasg.xjx.serdes.deserialize.MapRootSaxHandler;
import io.jonasg.xjx.serdes.deserialize.PathBasedSaxHandler;
import io.jonasg.xjx.serdes.deserialize.PathWriterIndexFactory;
import io.jonasg.xjx.serdes.deserialize.XjxDeserializationException;
import io.jonasg.xjx.serdes.seraialize.XmlNodeStructureFactory;
import io.jonasg.xjx.serdes.seraialize.XmlStringBuilder;

import java.io.Reader;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
import io.jonasg.xjx.serdes.serialize.XmlNodeStructureFactory;
import io.jonasg.xjx.serdes.serialize.XmlStringBuilder;

/**
* XjxSerdes provides functionality for serializing and deserializing objects to and from XML.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package io.jonasg.xjx.serdes.deserialize;

import io.jonasg.xjx.sax.Attribute;
import io.jonasg.xjx.sax.SaxHandler;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import io.jonasg.xjx.sax.Attribute;
import io.jonasg.xjx.sax.SaxHandler;

public class MapRootSaxHandler implements SaxHandler {

private final LinkedList<Map<String, Object>> mapsStack;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.jonasg.xjx.serdes.deserialize;

import java.util.HashMap;
import java.util.Map;

public record MapWithTypeInfo(Map<String, Object> map, Class<?> valueType) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package io.jonasg.xjx.serdes.deserialize;

import io.jonasg.xjx.sax.Attribute;
import io.jonasg.xjx.sax.SaxHandler;
import io.jonasg.xjx.serdes.Path;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import io.jonasg.xjx.sax.Attribute;
import io.jonasg.xjx.sax.SaxHandler;
import io.jonasg.xjx.serdes.Path;

public class PathBasedSaxHandler<T> implements SaxHandler {

private final Function<String, Map<Path, PathWriter>> indexSupplier;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
package io.jonasg.xjx.serdes.deserialize;

import io.jonasg.xjx.serdes.Path;
import io.jonasg.xjx.serdes.Tag;
import io.jonasg.xjx.serdes.TypeMappers;
import io.jonasg.xjx.serdes.deserialize.accessor.FieldAccessor;
import io.jonasg.xjx.serdes.reflector.FieldReflector;
import io.jonasg.xjx.serdes.reflector.TypeReflector;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
Expand All @@ -24,9 +17,16 @@
import java.util.Set;
import java.util.function.Supplier;

import io.jonasg.xjx.serdes.Path;
import io.jonasg.xjx.serdes.Tag;
import io.jonasg.xjx.serdes.TypeMappers;
import io.jonasg.xjx.serdes.deserialize.accessor.FieldAccessor;
import io.jonasg.xjx.serdes.reflector.FieldReflector;
import io.jonasg.xjx.serdes.reflector.TypeReflector;

public class PathWriterIndexFactory {

private static final List<Class<?>> BASIC_TYPES = List.of(
public static final List<Class<?>> BASIC_TYPES = List.of(
String.class, Integer.class, Boolean.class, boolean.class, Long.class, long.class, BigDecimal.class, Double.class,
double.class, char.class, Character.class, LocalDate.class, LocalDateTime.class, ZonedDateTime.class);
private final Map<Class<?>, Object> collectionCacheType = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package io.jonasg.xjx.serdes.deserialize;

import io.jonasg.xjx.sax.Attribute;
import io.jonasg.xjx.sax.SaxHandler;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import io.jonasg.xjx.sax.Attribute;
import io.jonasg.xjx.sax.SaxHandler;

public class TypedValueMapSaxHandler implements SaxHandler {

private final LinkedList<Map<String, Object>> mapsStack;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package io.jonasg.xjx.serdes.deserialize.accessor;

import io.jonasg.xjx.serdes.reflector.FieldReflector;

import java.util.function.Function;

import io.jonasg.xjx.serdes.reflector.FieldReflector;

public class ReflectiveFieldAccessor implements FieldAccessor {

private final FieldReflector field;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package io.jonasg.xjx.serdes.deserialize.accessor;

import io.jonasg.xjx.serdes.reflector.FieldReflector;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

import io.jonasg.xjx.serdes.reflector.FieldReflector;

public class SetterFieldAccessor implements FieldAccessor {

private final Object instance;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ public Class<?> type() {
return fieldReflector.type();
}

public InstanceReflector<Object> reflect() {
return new InstanceReflector<>(getValue());
}

@Override
public String toString() {
return new StringJoiner(", ", InstanceField.class.getSimpleName() + "[", "]")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,17 @@ public void setField(String fieldName, Object value) {
.ifPresent(f -> f.set(instance, value));
}

public List<InstanceField> fields(Predicate<InstanceField> predicate) {
return typeReflector.fields()
.stream()
.map(f -> new InstanceField(f, instance))
.filter(predicate)
.toList();
}
public List<InstanceField> fields(Predicate<InstanceField> predicate) {
return typeReflector.fields()
.stream()
.map(f -> new InstanceField(f, instance))
.filter(predicate)
.toList();
}

public List<InstanceField> fields() {
return fields(f -> true);
}

public T instance() {
return instance;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package io.jonasg.xjx.serdes.seraialize;

import io.jonasg.xjx.Attributes;
package io.jonasg.xjx.serdes.serialize;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import io.jonasg.xjx.Attributes;

public final class XmlNode {
private final String name;
private Object value;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package io.jonasg.xjx.serdes.serialize;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.List;

import io.jonasg.xjx.serdes.Path;
import io.jonasg.xjx.serdes.Section;
import io.jonasg.xjx.serdes.Tag;
import io.jonasg.xjx.serdes.reflector.InstanceField;
import io.jonasg.xjx.serdes.reflector.Reflector;

public class XmlNodeStructureFactory {

public static final List<Class<?>> BASIC_TYPES = List.of(
String.class, Integer.class, Boolean.class, boolean.class, Long.class, long.class, BigDecimal.class, Double.class,
double.class, char.class, Character.class, LocalDate.class, LocalDateTime.class, ZonedDateTime.class, byte[].class);

public <T> XmlNode build(T data) {
return getXmlNode(Path.parse("/"), data, null);
}

private <T> XmlNode getXmlNode(Path parentPath, T data, XmlNode node) {
if (data != null && !BASIC_TYPES.contains(data.getClass()) && !data.getClass().isEnum()) {
List<InstanceField> fields = Reflector.reflect(data).fields();
node = buildNodeForFields(parentPath, node, fields);
}
return node;
}

private XmlNode buildNodeForFields(Path parentPath, XmlNode node, List<InstanceField> fields) {
for (InstanceField field : fields) {
if (field.hasAnnotation(Tag.class)) {
node = buildNodeForField(field, parentPath, node);
}
else if (!BASIC_TYPES.contains(field.type())) {
return buildNodeForFields(parentPath, node, field.reflect().fields());
}
}
return node;
}

private XmlNode buildNodeForField(InstanceField field, Path parentPath, XmlNode rootNode) {
var tag = field.getAnnotation(Tag.class);
var path = parentPath.append(Path.parse(tag.path()));
if (rootNode == null) {
rootNode = new XmlNode(path.getRoot());
}
var node = rootNode;
for (int i = 1; i < path.size(); i++) {
Section section = path.getSection(i);
if (section.isLeaf()) {
handleLeafNode(field, section, tag, node);
}
else {
node = node.addNode(section.name());
}
}
return getXmlNode(path, field.getValue(), rootNode);
}

private void handleLeafNode(InstanceField field, Section section, Tag tag, XmlNode node) {
if (!tag.attribute().isEmpty()) {
if (field.getValue() != null) {
node.addNode(section.name())
.addAttribute(tag.attribute(), field.getValue());
}
}
else {
node.addValueNode(section.name(), field.getValue());
}
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
package io.jonasg.xjx.serdes.seraialize;

import io.jonasg.xjx.serdes.seraialize.XmlNode;
package io.jonasg.xjx.serdes.serialize;

import java.util.List;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package io.jonasg.xjx.serdes.deserialize;

import io.jonasg.xjx.serdes.Tag;
import io.jonasg.xjx.serdes.XjxSerdes;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

import io.jonasg.xjx.serdes.Tag;
import io.jonasg.xjx.serdes.XjxSerdes;

public class ComplexTypeDeserializationTest {
@Test
void mapFieldsOnComplexType() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package io.jonasg.xjx.serdes.deserialize;

import io.jonasg.xjx.serdes.Tag;
import io.jonasg.xjx.serdes.XjxSerdes;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

import io.jonasg.xjx.serdes.Tag;
import io.jonasg.xjx.serdes.XjxSerdes;

public class CustomValueDeserializationTest {

@Test
Expand Down
Loading

0 comments on commit 3b22c7d

Please sign in to comment.