Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XML to JSON legacy component - corrections/refinements #74

Merged
merged 23 commits into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
be9b718
use attribute names from Contants class
brunovg Nov 10, 2023
aecbfca
correction how the attributes names are added on object node
brunovg Nov 10, 2023
cfbe197
added more unit tests
brunovg Nov 10, 2023
ff4a722
included test unit examples 17 to 26
brunovg Nov 14, 2023
40ddee9
testing some changes on xml to json conversion
brunovg Nov 14, 2023
de6ae23
by default do nothing. when it's a null in the string, set value as null
brunovg Nov 14, 2023
966b953
include string attribute type as default option, and use attribute ty…
brunovg Nov 15, 2023
deaaef3
if subNode is empty, then set empty array node in rootObjectNode
brunovg Nov 16, 2023
e97b363
an xml node that contains text and attributes, converts ok to json, w…
brunovg Nov 16, 2023
1fc8ada
if attribute type is empty, act as string. if something else, do nothing
brunovg Nov 20, 2023
b0cbf60
methods to check if siblings/children have the same name
brunovg Nov 21, 2023
1eede80
using areSiblingsNamesEqual info on TextNodeTransaction
brunovg Nov 21, 2023
45e8cee
extracting attribute type info by Element or using directly by value
brunovg Nov 21, 2023
40ab38d
set empty array node in rootObjectNode only if there's no type attrib…
brunovg Dec 18, 2023
8436f6a
added test unit 27
brunovg Dec 21, 2023
fe24c79
new argument grandParentClass added in the main method convertXmlToJs…
brunovg Dec 21, 2023
e2dc829
added more unit tests
brunovg Jan 3, 2024
76bbb0f
refined logic on OtherTypeWithoutNamespace to extractChildAsOtherInOb…
brunovg Jan 3, 2024
7cfb9d6
refined logic on RootArrayType to use recursive call convertXmlToJson…
brunovg Jan 3, 2024
c729a70
refined logic on OtherType to use #text when type attribute is not null
brunovg Jan 3, 2024
9691784
refined login on method isRootArray
brunovg Jan 3, 2024
2b8729a
refined method getElementName, do not use skipNamespaces in the if logic
brunovg Jan 3, 2024
f9fad2b
multiple refinements/corrections
brunovg Jan 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
package org.assimbly.xmltojsonlegacy;

import java.util.Arrays;
import java.util.List;

public class Constants {

public static final String JSON_XML_ATTR_PREFIX = "@";
public static final String JSON_XML_TEXT_FIELD = "#text";

public static final String JSON_XML_ATTR_CLASS = "class";
public static final String JSON_XML_ATTR_CONTEXT = "context";

public static final String JSON_XML_ATTR_TYPE = "type";

public static final String JSON_XML_ATTR_TYPE_NUMBER = "number";
public static final String JSON_XML_ATTR_TYPE_BOOLEAN = "boolean";
public static final String JSON_XML_ATTR_TYPE_STRING = "string";
public static final String JSON_XML_ATTR_TYPE_ARRAY = "array";
public static final String JSON_XML_ATTR_TYPE_OBJECT = "object";

public static final List<String> SPECIAL_JSON_XML_ATTR_TYPES = Arrays.asList(
Constants.JSON_XML_ATTR_CLASS,
Constants.JSON_XML_ATTR_CONTEXT,
Constants.JSON_XML_ATTR_TYPE
);

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public void marshal(Exchange exchange, Object graph, OutputStream stream) throws

XmlToJsonProcessor xmlToJsonJsonProcessor = new XmlToJsonProcessor(this);
JsonNode jsonNodeResp = xmlToJsonJsonProcessor.convertXmlToJson(
document.getDocumentElement(), 0, null, 0, true, null
document.getDocumentElement(), 0, null, null, 0, true, null
);

setContentTypeHeader(exchange, MediaType.APPLICATION_JSON_VALUE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class Print {

// print some information about a specific tree level
public static void elementDetails(
Element element, int level, String parentClass, int parentSiblings, boolean isRootArray, boolean isOneValue,
Element element, int level, String grandParentClass, String parentClass, int parentSiblings, boolean isRootArray, boolean isOneValue,
boolean isObject, boolean isFirstChild, int numberOfSiblings, int numberOfChildren, String classAttr,
String typeAttr, String namespace, boolean removeNamespacePrefixes, boolean skipNamespaces
) {
Expand All @@ -21,6 +21,7 @@ public static void elementDetails(
);
data(" typeAttr: " + typeAttr, level);
data(" classAttr: " + classAttr, level);
data(" grandParentClass: " + grandParentClass, level);
data(" parentClass: " + parentClass, level);
data(" numberOfChildren: " + numberOfChildren, level);
data(" numberOfSiblings: " + numberOfSiblings, level);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.assimbly.xmltojsonlegacy.processor.xmltojson.textnode.TextNodeTransactionFactory;
import org.assimbly.xmltojsonlegacy.utils.ElementUtils;
import org.assimbly.xmltojsonlegacy.utils.ElementChecker;
import org.assimbly.xmltojsonlegacy.utils.ExtractUtils;
import org.w3c.dom.*;

public class XmlToJsonProcessor {
Expand All @@ -28,7 +29,7 @@ public XmlToJsonProcessor(CustomXmlJsonDataFormat xmlJsonDataFormat) {

// convert xml to json
public static JsonNode convertXmlToJson(
Element element, int level, String parentClass, int parentSiblings, boolean isFirstChild, String namespace
Element element, int level, String grandParentClass, String parentClass, int parentSiblings, boolean isFirstChild, String namespace
) {
ObjectNode rootObjectNode = JsonNodeFactory.instance.objectNode();
ArrayNode rootArrayNode = JsonNodeFactory.instance.arrayNode();
Expand All @@ -42,10 +43,12 @@ public static JsonNode convertXmlToJson(

boolean hasAttributes = element.hasAttributes();
boolean isRootNode = (level == 0);
boolean areSiblingsNamesEqual = ElementUtils.areSiblingsNamesEqual(element);
boolean areChildrenNamesEqual = ElementUtils.areChildrenNamesEqual(element);
namespace = (isRootNode ? ElementUtils.getNamespace(element) : namespace);
boolean isRootArray = ElementChecker.isRootArray(
level, numberOfChildren, numberOfSiblings, parentSiblings, classAttr, parentClass, hasAttributes,
elementDeepestDepth, namespace, xmlJsonDataFormat.isTypeHints()
elementDeepestDepth, namespace, xmlJsonDataFormat.isTypeHints(), areChildrenNamesEqual
);
boolean isObject = ElementChecker.isObject(elementDeepestDepth, numberOfChildren, classAttr);
boolean isOneValue = ElementChecker.isOneValue(level, numberOfSiblings, parentClass, elementDeepestDepth);
Expand All @@ -57,7 +60,7 @@ public static JsonNode convertXmlToJson(
);

Print.elementDetails(
element, level, parentClass, parentSiblings, isRootArray, isOneValue, isObject, isFirstChild,
element, level, grandParentClass, parentClass, parentSiblings, isRootArray, isOneValue, isObject, isFirstChild,
numberOfSiblings, numberOfChildren, classAttr, typeAttr, namespace,
xmlJsonDataFormat.isRemoveNamespacePrefixes(), xmlJsonDataFormat.isSkipNamespaces()
);
Expand All @@ -79,7 +82,7 @@ public static JsonNode convertXmlToJson(
nodeCount++;
JsonNode processNodeResp = processElementNode(
childNode, rootArrayNode, rootObjectNode, nodeCount, level, numberOfChildren, numberOfSiblings,
classAttr, isRootArray, isObject, isSingleChildren, isFirstChild, namespace
parentClass, classAttr, isRootArray, isObject, isSingleChildren, isFirstChild, namespace
);
if (processNodeResp != null)
return processNodeResp;
Expand All @@ -94,8 +97,12 @@ public static JsonNode convertXmlToJson(
} else {
JsonNode processTextResp = processTextNode(
childNode, element, rootArrayNode, rootObjectNode, level, index, nodeListSize,
isRootArray, isRootNode, isObject, isOneValue, namespace
grandParentClass, parentClass, isRootArray, isRootNode, isObject, isOneValue,
namespace, areSiblingsNamesEqual
);
if(ExtractUtils.rootObjectNodeContainsTextAttribute(rootObjectNode)) {
isRootArray = false;
}
if (processTextResp != null)
return processTextResp;
}
Expand All @@ -113,41 +120,48 @@ public static JsonNode convertXmlToJson(
// process an element node of type Node
private static JsonNode processElementNode(
Node childNode, ArrayNode rootArrayNode, ObjectNode rootObjectNode, int nodeCount, int level,
int numberOfChildren, int numberOfSiblings, String classAttr, boolean isRootArray, boolean isObject,
boolean isSingleChildren, boolean isFirstChild, String namespace
int numberOfChildren, int numberOfSiblings, String parentClass, String classAttr,
boolean isRootArray, boolean isObject, boolean isSingleChildren, boolean isFirstChild, String namespace
) {
boolean isFirstSibling = ElementChecker.isFirstSiblingByNumCounts(nodeCount);
Element childElement = (Element) childNode;

ElementNodeTransaction transactionProcessor = ElementNodeTransactionFactory.getProcessorFor(isObject, isRootArray, namespace);
return transactionProcessor.process(
childNode, rootArrayNode, rootObjectNode, nodeCount, level, numberOfChildren, numberOfSiblings,
classAttr, isRootArray, isObject, isSingleChildren, isFirstChild, isFirstSibling, childElement,
namespace, xmlJsonDataFormat.isTrimSpaces(), xmlJsonDataFormat.isSkipNamespaces(),
parentClass, classAttr, isRootArray, isObject, isSingleChildren, isFirstChild, isFirstSibling,
childElement, namespace, xmlJsonDataFormat.isTrimSpaces(), xmlJsonDataFormat.isSkipNamespaces(),
xmlJsonDataFormat.isRemoveNamespacePrefixes(), xmlJsonDataFormat.isTypeHints()
);
}

// process an element node of type Text
private static JsonNode processTextNode(
Node childNode, Element element, ArrayNode rootArrayNode, ObjectNode rootObjectNode, int level, int index,
int nodeListSize, boolean isRootArray, boolean isRootNode, boolean isObject, boolean isOneValue, String namespace
int nodeListSize, String grandParentClass, String parentClass, boolean isRootArray, boolean isRootNode,
boolean isObject, boolean isOneValue, String namespace, boolean areSiblingsNamesEqual
) {
TextNodeTransaction transactionProcessor = TextNodeTransactionFactory.getProcessorFor(isRootNode, isObject, isOneValue);
return transactionProcessor.process(childNode, element, rootArrayNode, rootObjectNode, level, index, nodeListSize,
isRootArray, isRootNode, isObject, isOneValue, namespace, xmlJsonDataFormat.isForceTopLevelObject(),
grandParentClass, parentClass, isRootArray, isRootNode, isObject, isOneValue, namespace, xmlJsonDataFormat.isForceTopLevelObject(),
xmlJsonDataFormat.isTrimSpaces(), xmlJsonDataFormat.isSkipNamespaces(),
xmlJsonDataFormat.isRemoveNamespacePrefixes(), xmlJsonDataFormat.isTypeHints()
xmlJsonDataFormat.isRemoveNamespacePrefixes(), xmlJsonDataFormat.isTypeHints(), areSiblingsNamesEqual
);
}

// add attributes in the object node
private static void addAttributesInObjectNode(Element element, ObjectNode rootObjectNode, boolean isAttributeObject) {
if(!xmlJsonDataFormat.isTypeHints() && !isAttributeObject && element.hasAttributes()){
if(!isAttributeObject && element.hasAttributes()){
NamedNodeMap attrMap = element.getAttributes();
for (int j = 0; j < attrMap.getLength(); j++) {
Node node = attrMap.item(j);
rootObjectNode.put(Constants.JSON_XML_ATTR_PREFIX+node.getNodeName(), node.getNodeValue());
String attr = node.getNodeName();
if((!xmlJsonDataFormat.isTypeHints() && !ElementUtils.isAnXmlnsAttribute(attr)) ||
xmlJsonDataFormat.isTypeHints() && !ElementUtils.isAnSpecialAttribute(attr) &&
!ElementUtils.isAnXmlnsAttribute(attr)
) {
rootObjectNode.put(Constants.JSON_XML_ATTR_PREFIX+node.getNodeName(), node.getNodeValue());
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ public interface ElementNodeTransaction {

JsonNode process(
Node childNode, ArrayNode rootArrayNode, ObjectNode rootObjectNode, int nodeCount, int level,
int numberOfChildren, int numberOfSiblings, String classAttr, boolean isRootArray, boolean isObject,
boolean isSingleChildren, boolean isFirstChild, boolean isFirstSibling, Element childElement,
String namespace, boolean trimSpaces, boolean skipNamespaces, boolean removeNamespacePrefixes, boolean typeHints
int numberOfChildren, int numberOfSiblings, String parentClass, String classAttr, boolean isRootArray,
boolean isObject, boolean isSingleChildren, boolean isFirstChild, boolean isFirstSibling,
Element childElement, String namespace, boolean trimSpaces, boolean skipNamespaces,
boolean removeNamespacePrefixes, boolean typeHints
);

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ public class ObjectType implements ElementNodeTransaction {
@Override
public JsonNode process(
Node childNode, ArrayNode rootArrayNode, ObjectNode rootObjectNode, int nodeCount, int level,
int numberOfChildren, int numberOfSiblings, String classAttr, boolean isRootArray, boolean isObject,
boolean isSingleChildren, boolean isFirstChild, boolean isFirstSibling, Element childElement,
int numberOfChildren, int numberOfSiblings, String parentClass, String classAttr, boolean isRootArray,
boolean isObject, boolean isSingleChildren, boolean isFirstChild, boolean isFirstSibling, Element childElement,
String namespace, boolean trimSpaces, boolean skipNamespaces, boolean removeNamespacePrefixes, boolean typeHints
) {
// extract child as an object
Print.data(" 1. IS OBJECT", level);
ExtractUtils.extractChildAsObject(
level, rootObjectNode, numberOfSiblings, classAttr, childElement, isFirstSibling, namespace,
level, rootObjectNode, numberOfSiblings, parentClass, classAttr, childElement, isFirstSibling, namespace,
trimSpaces, skipNamespaces, removeNamespacePrefixes, typeHints
);
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ public class OtherTypeWithNamespace implements ElementNodeTransaction {
@Override
public JsonNode process(
Node childNode, ArrayNode rootArrayNode, ObjectNode rootObjectNode, int nodeCount, int level,
int numberOfChildren, int numberOfSiblings, String classAttr, boolean isRootArray, boolean isObject,
boolean isSingleChildren, boolean isFirstChild, boolean isFirstSibling, Element childElement,
int numberOfChildren, int numberOfSiblings, String parentClass, String classAttr, boolean isRootArray,
boolean isObject, boolean isSingleChildren, boolean isFirstChild, boolean isFirstSibling, Element childElement,
String namespace, boolean trimSpaces, boolean skipNamespaces, boolean removeNamespacePrefixes, boolean typeHints
) {
ExtractUtils.extractChildAsOtherInArrayNode(
level, rootArrayNode, numberOfSiblings, classAttr, (Element) childNode, childElement,
level, rootArrayNode, numberOfSiblings, parentClass, classAttr, (Element) childNode, childElement,
isFirstSibling, namespace
);
rootObjectNode.set(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.commons.lang3.StringUtils;
import org.assimbly.xmltojsonlegacy.Constants;
import org.assimbly.xmltojsonlegacy.processor.xmltojson.elementnode.ElementNodeTransaction;
import org.assimbly.xmltojsonlegacy.utils.ElementChecker;
import org.assimbly.xmltojsonlegacy.utils.ElementUtils;
import org.assimbly.xmltojsonlegacy.utils.ExtractUtils;
import org.w3c.dom.Element;
Expand All @@ -14,22 +17,25 @@ public class OtherTypeWithoutNamespace implements ElementNodeTransaction {
@Override
public JsonNode process(
Node childNode, ArrayNode rootArrayNode, ObjectNode rootObjectNode, int nodeCount, int level,
int numberOfChildren, int numberOfSiblings, String classAttr, boolean isRootArray, boolean isObject,
boolean isSingleChildren, boolean isFirstChild, boolean isFirstSibling, Element childElement,
int numberOfChildren, int numberOfSiblings, String parentClass, String classAttr, boolean isRootArray,
boolean isObject, boolean isSingleChildren, boolean isFirstChild, boolean isFirstSibling, Element childElement,
String namespace, boolean trimSpaces, boolean skipNamespaces, boolean removeNamespacePrefixes, boolean typeHints
) {
if(!typeHints) {
// extract child as other type and add into the array node
int children = ElementUtils.calculateNumberOfChildren(childElement);
boolean classAttrOnChildElementIsNUll = ElementChecker.isElementAttributeNull(childElement, Constants.JSON_XML_ATTR_CLASS);
if((level == 0 && numberOfChildren > 1) ||
(numberOfChildren == 1 && ElementUtils.calculateNumberOfChildren(childElement) > 1)
(numberOfChildren == 1 && children >= 1 && !classAttrOnChildElementIsNUll) ||
!ElementUtils.areSiblingsNamesEqual(childElement)
) {
ExtractUtils.extractChildAsOtherInObjectNode(
level, rootObjectNode, numberOfSiblings, classAttr, (Element) childNode, childElement,
level, rootObjectNode, numberOfSiblings, parentClass, classAttr, (Element) childNode, childElement,
isFirstSibling, namespace, skipNamespaces, removeNamespacePrefixes
);
} else {
ExtractUtils.extractChildAsOtherInArrayNode(
level, rootArrayNode, numberOfSiblings, classAttr, (Element) childNode, childElement,
level, rootArrayNode, numberOfSiblings, parentClass, classAttr, (Element) childNode, childElement,
isFirstSibling, namespace
);
rootObjectNode.set(
Expand All @@ -40,7 +46,7 @@ public JsonNode process(
} else {
// extract child as other type and add into the object node
ExtractUtils.extractChildAsOtherInObjectNode(
level, rootObjectNode, numberOfSiblings, classAttr, (Element) childNode, childElement,
level, rootObjectNode, numberOfSiblings, parentClass, classAttr, (Element) childNode, childElement,
isFirstSibling, namespace, skipNamespaces, removeNamespacePrefixes
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.commons.lang3.StringUtils;
import org.assimbly.xmltojsonlegacy.logs.Print;
import org.assimbly.xmltojsonlegacy.processor.XmlToJsonProcessor;
import org.assimbly.xmltojsonlegacy.processor.xmltojson.elementnode.ElementNodeTransaction;
Expand All @@ -15,17 +16,17 @@ public class RootArrayType implements ElementNodeTransaction {
@Override
public JsonNode process(
Node childNode, ArrayNode rootArrayNode, ObjectNode rootObjectNode, int nodeCount, int level,
int numberOfChildren, int numberOfSiblings, String classAttr, boolean isRootArray, boolean isObject,
boolean isSingleChildren, boolean isFirstChild, boolean isFirstSibling, Element childElement,
int numberOfChildren, int numberOfSiblings, String parentClass, String classAttr, boolean isRootArray,
boolean isObject, boolean isSingleChildren, boolean isFirstChild, boolean isFirstSibling, Element childElement,
String namespace, boolean trimSpaces, boolean skipNamespaces, boolean removeNamespacePrefixes, boolean typeHints
) {
Print.data(" 1. IS ROOT ARRAY", level);
if(isSingleChildren && isFirstChild) {
if(isSingleChildren && isFirstChild && StringUtils.isNotEmpty(parentClass)) {
// recursive call with child element
return XmlToJsonProcessor.convertXmlToJson(childElement, level +1, classAttr, numberOfSiblings, isFirstSibling, namespace);
return XmlToJsonProcessor.convertXmlToJson(childElement, level +1, parentClass, classAttr, numberOfSiblings, isFirstSibling, namespace);
} else {
// extract child as an array
ExtractUtils.extractChildAsArray(level, rootArrayNode, numberOfSiblings, classAttr, childElement, isFirstSibling, namespace);
ExtractUtils.extractChildAsArray(level, rootArrayNode, numberOfSiblings, parentClass, classAttr, childElement, isFirstSibling, namespace);
}
return null;
}
Expand Down
Loading