
xmlparser.XmlReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of simplexml Show documentation
Show all versions of simplexml Show documentation
A clean and simple XML parser, serializer, and deserializer.
The newest version!
package xmlparser;
import xmlparser.annotations.*;
import xmlparser.error.InvalidAnnotation;
import xmlparser.error.InvalidObject;
import xmlparser.error.InvalidXPath;
import xmlparser.error.InvalidXml;
import xmlparser.model.XmlElement;
import xmlparser.parsing.DomBuilder;
import xmlparser.parsing.ObjectDeserializer;
import xmlparser.utils.AccessDeserializers;
import xmlparser.utils.Escaping.UnEscape;
import xmlparser.utils.Trimming.Trim;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.*;
import java.util.*;
import java.util.regex.Pattern;
import static org.objenesis.ObjenesisHelper.newInstance;
import static xmlparser.model.XmlElement.findChildForName;
import static xmlparser.utils.Functions.isNullOrEmpty;
import static xmlparser.utils.Reflection.*;
import static xmlparser.utils.Validator.multipleAreNotNull;
import static xmlparser.xpath.XPathExpression.newXPath;
public interface XmlReader extends AccessDeserializers {
default T domToObject(final XmlElement node, final Class clazz) throws InvalidXPath {
if (node == null) return null;
final ObjectDeserializer c = getDeserializer(clazz);
if (c != null) return c.convert(node, clazz);
final T object = clazz.isRecord() ? domToRecord(node, clazz) : domToClass(node, clazz);
for (final Method method : clazz.getDeclaredMethods()) {
if (!method.isAnnotationPresent(XmlObjectValidator.class))
continue;
try {
method.invoke(object);
} catch (final Exception e) {
throw new InvalidObject(clazz, e);
}
}
return object;
}
private T domToRecord(final XmlElement node, final Class clazz) {
final String parentName = toName(clazz);
XmlElement selectedNode;
final RecordComponent[] fields = clazz.getRecordComponents();
final Object[] fieldValues = new Object[fields.length];
for (int i = 0; i < fields.length; i++) {
final RecordComponent f = fields[i];
if (f.isAnnotationPresent(XmlNoImport.class)) continue;
selectedNode = node;
if (f.isAnnotationPresent(XmlPath.class)) {
selectedNode = newXPath(parentName + "/" + f.getAnnotation(XmlPath.class).value()).evaluateAny(node);
}
final Object fieldValue = switch (toFieldType(f)) {
case FIELD_DESERIALIZER -> invokeFieldDeserializer(f, selectedNode);
case TEXTNODE -> textNodeToValue(toPattern(f), f.getType(), selectedNode);
case ANNOTATED_ATTRIBUTE -> attributeToValue(toPattern(f), f.getType(), toName(f), deWrap(selectedNode, f));
case SET -> domToSet(f, toClassOfCollection(f), toName(f), deWrap(selectedNode, f));
case LIST -> domToList(f, toClassOfCollection(f), toName(f), deWrap(selectedNode, f));
case ARRAY -> domToArray(f.getType().getComponentType(), toName(f), deWrap(selectedNode, f));
case MAP -> domToMap(f, (ParameterizedType) f.getGenericType(), toName(f), deWrap(selectedNode, f));
case ENUM -> enumNodeToValue(toEnumType(f), toName(f), deWrap(selectedNode, f));
default -> {
final String name = toName(f);
final String value = selectedNode.attributes.get(name);
if (value != null) {
yield stringToValue(f.getType(), value);
}
final var isAbstract = f.getAnnotation(XmlAbstractClass.class);
if (isAbstract != null) {
final XmlElement child = selectedNode.findChildForName(name, null);
yield domToObject(child, findAbstractType(isAbstract, child));
}
yield domToObject(findChildForName(deWrap(selectedNode, f), name, null), f.getType());
}
};
fieldValues[i] = fieldValue;
}
try {
return canonicalConstructorOfRecord(clazz).newInstance(fieldValues);
} catch (final InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new SecurityException("Canonical constructor of record could not be invoked", e);
}
}
private T domToClass(final XmlElement node, final Class clazz) {
final T o = newInstance(clazz);
final String parentName = toName(clazz);
XmlElement selectedNode;
for (final Field f : listFields(clazz)) {
f.setAccessible(true);
if (Modifier.isStatic(f.getModifiers())) continue;
if (f.isAnnotationPresent(XmlNoImport.class)) continue;
selectedNode = node;
if (f.isAnnotationPresent(XmlPath.class)) {
selectedNode = newXPath(parentName + "/" + f.getAnnotation(XmlPath.class).value()).evaluateAny(node);
}
final Object fieldValue = switch (toFieldType(f)) {
case FIELD_DESERIALIZER -> invokeFieldDeserializer(f, selectedNode);
case TEXTNODE -> textNodeToValue(toPattern(f), f.getType(), selectedNode);
case ANNOTATED_ATTRIBUTE -> attributeToValue(toPattern(f), f.getType(), toName(f), deWrap(selectedNode, f));
case SET -> domToSet(f, toClassOfCollection(f), toName(f), deWrap(selectedNode, f));
case LIST -> domToList(f, toClassOfCollection(f), toName(f), deWrap(selectedNode, f));
case ARRAY -> domToArray(f.getType().getComponentType(), toName(f), deWrap(selectedNode, f));
case MAP -> domToMap(f, (ParameterizedType) f.getGenericType(), toName(f), deWrap(selectedNode, f));
case ENUM -> enumNodeToValue(toEnumType(f), toName(f), deWrap(selectedNode, f));
default -> {
final String name = toName(f);
if (selectedNode == null) yield null;
final String value = selectedNode.attributes.get(name);
if (value != null) {
yield stringToValue(f.getType(), value);
}
final var isAbstract = f.getAnnotation(XmlAbstractClass.class);
if (isAbstract != null) {
final XmlElement child = selectedNode.findChildForName(name, null);
yield domToObject(child, findAbstractType(isAbstract, child));
}
yield domToObject(findChildForName(deWrap(selectedNode, f), name, null), f.getType());
}
};
setField(f, o, fieldValue);
}
return o;
}
private XmlElement deWrap(final XmlElement element, final Field field) {
if (!isWrapped(field)) return element;
return element.findChildForName(toWrappedName(field), null);
}
private XmlElement deWrap(final XmlElement element, final RecordComponent field) {
if (!isWrapped(field)) return element;
return element.findChildForName(toWrappedName(field), null);
}
private Object textNodeToValue(final String pattern, final Class> type, final XmlElement node) {
final ObjectDeserializer conv = getDeserializer(type);
final Object value = (conv != null) ? conv.convert(node) : null;
if (!isNullOrEmpty(pattern) && value != null)
validatePattern(pattern, value);
return value;
}
private Object enumNodeToValue(final Class extends Enum> type, final String name, final XmlElement node) {
final XmlElement text = findChildForName(node, name, null);
if (text == null) return null;
final String value = text.getText();
if (value == null) return null;
final ObjectDeserializer conv = getDeserializer(type);
return (conv == null) ? valueOfEnum(type, value) : conv.convert(node);
}
boolean isEnumCachingEnabled();
Map, Map> getEnumCache();
private Map getEnumValueDirectory(final Class type) {
final boolean enumCaching = isEnumCachingEnabled();
if (enumCaching) {
final Map, Map> cache = getEnumCache();
return cache.computeIfAbsent(type, this::newEnumValueMap);
}
return newEnumValueMap(type);
}
private Map newEnumValueMap(final Class type) {
final Map valueMap = new HashMap<>();
for (final T t : type.getEnumConstants()) {
try {
final XmlEnumValue annotation = type.getField(t.name()).getAnnotation(XmlEnumValue.class);
valueMap.put(annotation != null ? annotation.value() : t.name(), t);
} catch (final NoSuchFieldException e) {
// impossible
throw new RuntimeException(e);
}
}
return valueMap;
}
private T valueOfEnum(final Class type, final String value) {
final T t = getEnumValueDirectory(type).get(value);
if (t == null) throw new IllegalArgumentException("No enum constant for " + type.getName() + "." + value);
return t;
}
private Object attributeToValue(final String pattern, final Class> type, final String name, final XmlElement node) {
if (node == null) return null;
final ObjectDeserializer conv = getDeserializer(type);
if (conv == null) return null;
final String value = node.attributes.get(name);
if (value == null) return null;
if (!isNullOrEmpty(pattern))
validatePattern(pattern, value);
return conv.convert(value);
}
private Object stringToValue(final Class> type, final String value) {
final ObjectDeserializer conv = getDeserializer(type);
return (conv != null) ? conv.convert(value) : null;
}
private Set
© 2015 - 2025 Weber Informatics LLC | Privacy Policy