diff --git a/pom.xml b/pom.xml
index e0a4066..9734d0b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
org.wso2
soaptorest
- 1.3
+ 1.4
diff --git a/src/main/java/org/wso2/soaptorest/OASGenerator.java b/src/main/java/org/wso2/soaptorest/OASGenerator.java
index 2ab8137..b941d62 100644
--- a/src/main/java/org/wso2/soaptorest/OASGenerator.java
+++ b/src/main/java/org/wso2/soaptorest/OASGenerator.java
@@ -30,6 +30,7 @@
import org.wso2.soaptorest.utils.SOAPToRESTConstants;
import javax.xml.namespace.QName;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -265,6 +266,15 @@ private static void processXSSequence(XSSequence xsSequence, Schema> parentSch
for (XSElement xsElement : xsElementList) {
Schema> innerSchema = getSchemaForXSElement(xsElement, isElementFormDefaultQualified);
if (innerSchema != null) {
+ // if element is not optional, add it to the required list of parent schema
+ boolean isRef = xsElement.getRefKey() != null && xsElement.getName() == null;
+ if (!xsElement.isOptional() && !isRef) {
+ if (parentSchema.getRequired() != null) {
+ parentSchema.getRequired().add(xsElement.getName().getLocalPart());
+ } else {
+ parentSchema.setRequired(Arrays.asList(xsElement.getName().getLocalPart()));
+ }
+ }
parentSchema.addProperties(innerSchema.getName(), innerSchema);
}
}
diff --git a/src/main/java/org/wso2/soaptorest/SOAPRequestBodyGenerator.java b/src/main/java/org/wso2/soaptorest/SOAPRequestBodyGenerator.java
index 310a2cd..f297d4d 100644
--- a/src/main/java/org/wso2/soaptorest/SOAPRequestBodyGenerator.java
+++ b/src/main/java/org/wso2/soaptorest/SOAPRequestBodyGenerator.java
@@ -36,6 +36,7 @@
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
import org.wso2.soaptorest.exceptions.SOAPToRESTException;
import org.wso2.soaptorest.models.SOAPRequestElement;
import org.wso2.soaptorest.models.SOAPtoRESTConversionData;
@@ -51,6 +52,11 @@
import java.util.List;
import java.util.Map;
+import static org.wso2.soaptorest.utils.SOAPToRESTConstants.ATTRIBUTE_PLACEHOLDER;
+import static org.wso2.soaptorest.utils.SOAPToRESTConstants.IF_PLACEHOLDER;
+import static org.wso2.soaptorest.utils.SOAPToRESTConstants.IS_EMPTY_ATTRIBUTE;
+import static org.wso2.soaptorest.utils.SOAPToRESTConstants.QUESTION_MARK_PLACEHOLDER;
+
/**
* Class that reads OpenAPI and generate soap request payloads
*/
@@ -116,6 +122,7 @@ public static SOAPtoRESTConversionData generateSOAPtoRESTConversionObjectFromOAS
}
List parameters = operation.getParameters();
+ Map jsonPathAndSchemaMap = new HashMap<>();
if (parameters != null) {
for (Parameter parameter : parameters) {
String name = parameter.getName();
@@ -131,7 +138,7 @@ public static SOAPtoRESTConversionData generateSOAPtoRESTConversionObjectFromOAS
operation.getRequestBody().getContent().get(SOAPToRESTConstants.
DEFAULT_CONTENT_TYPE).getSchema();
Example example = ExampleBuilder.fromSchema(model, openAPI.getComponents().getSchemas());
- parameterJsonPathMapping = ListJSONPaths.getJsonPathsFromExample(example);
+ parameterJsonPathMapping = ListJSONPaths.getJsonPathsFromExample(example, jsonPathAndSchemaMap);
} catch (Exception e) {
throw new SOAPToRESTException("Cannot generate JSON body from the OpenAPI", e);
}
@@ -139,8 +146,9 @@ public static SOAPtoRESTConversionData generateSOAPtoRESTConversionObjectFromOAS
}
Document soapRequestBody = createSOAPRequestXMLForOperation(parameterJsonPathMapping, queryParameters,
- namespace, operationId, openAPI);
+ namespace, operationId, openAPI, jsonPathAndSchemaMap );
+ iterateChildNodes(soapRequestBody.getDocumentElement(), soapRequestBody);
requestBodies.put(operationId, new SOAPRequestElement(soapRequestBody, soapAction, namespace,
soapNamespace));
}
@@ -148,9 +156,59 @@ public static SOAPtoRESTConversionData generateSOAPtoRESTConversionObjectFromOAS
return new SOAPtoRESTConversionData(openAPI, requestBodies, soapService, soapPort);
}
+ /**
+ * Iterate through the given document and wrap the possible empty elements with <#if> statements.
+ * @param node Current node
+ * @param document Root document
+ */
+ private static void iterateChildNodes(Node node, Document document) {
+ // Get the child nodes of the current node
+ NodeList childNodes = node.getChildNodes();
+
+ // Iterate over the child nodes
+ for (int i = 0; i < childNodes.getLength(); i++) {
+ Node childNode = childNodes.item(i);
+
+ // Check if the element has the attribute "addIsEmptyCheck" with value "true"
+ if (childNode instanceof Element) {
+ Element element = (Element) childNode;
+ if (element.hasAttribute(IS_EMPTY_ATTRIBUTE) &&
+ element.getAttribute(IS_EMPTY_ATTRIBUTE).equals("true")) {
+ // Create a new element <#if>
+ String value = element.getTextContent();
+ String nodeValue = element.getNodeName();
+ Element nextSibling = (Element) element.getNextSibling();
+
+ // copy the element without attributes
+ Element modified = document.createElement(nodeValue);
+ modified.setTextContent(value);
+
+ Element newElement = document.createElement(IF_PLACEHOLDER);
+ // remove ${} from the value and append has_content check
+ newElement.setAttribute(ATTRIBUTE_PLACEHOLDER, value.substring(2,value.length()-1) +
+ QUESTION_MARK_PLACEHOLDER + "has_content");
+ newElement.appendChild(modified);
+
+ Node parentNode = element.getParentNode();
+ parentNode.removeChild(element);
+ if (nextSibling == null) {
+ parentNode.appendChild(newElement);
+ } else {
+ parentNode.insertBefore(newElement, nextSibling);
+ }
+ iterateChildNodes(newElement, document);
+ }
+ }
+
+ // Recursively iterate child nodes of non-matched elements
+ if (childNode instanceof Element && !childNode.getNodeName().equals(IF_PLACEHOLDER)) {
+ iterateChildNodes(childNode, document);
+ }
+ }
+ }
private static Document createSOAPRequestXMLForOperation(ArrayList parameterJsonPathMapping, Map queryPathParamMapping, String namespace, String operationId, OpenAPI openAPI) throws
- SOAPToRESTException {
+ String> queryPathParamMapping, String namespace, String operationId, OpenAPI openAPI,
+ Map jsonPathAndSchemaMap) throws SOAPToRESTException {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder;
@@ -204,6 +262,21 @@ private static Document createSOAPRequestXMLForOperation(ArrayList param
isNamespaceQualified = true;
}
}
+ Schema> parentSchema = null;
+ boolean needIsEmptyCheck = false;
+ // Check parent schema for required fields and wrap with isEmpty check if required
+ if (prevElement != null) {
+ String parentElementName = prevElement.getNodeName();
+ if (parentElementName.contains(SOAPToRESTConstants.NAMESPACE_SEPARATOR)) {
+ parentElementName = parentElementName.split(SOAPToRESTConstants.NAMESPACE_SEPARATOR)[1];
+ }
+ parentSchema = openAPI.getComponents().getSchemas().get(jsonPathAndSchemaMap.get(parentElementName));
+ if (parentSchema != null && parentSchema.getRequired() != null &&
+ !parentSchema.getRequired().contains(parameterTreeNode)) {
+ needIsEmptyCheck = true;
+ }
+ }
+
String payloadPrefix = "${";
if (StringUtils.isNotBlank(parameterTreeNode)) {
if (SOAPToRESTConstants.ATTR_CONTENT_KEYWORD.equalsIgnoreCase(parameterTreeNode)) {
@@ -255,6 +328,9 @@ private static Document createSOAPRequestXMLForOperation(ArrayList param
if (elemPos == length - 1) {
element.setTextContent(payloadPrefix + currentJSONPath + "}");
}
+ if (needIsEmptyCheck) {
+ element.setAttribute(IS_EMPTY_ATTRIBUTE, "true");
+ }
if (prevElement != null) {
prevElement.appendChild(element);
} else {
diff --git a/src/main/java/org/wso2/soaptorest/WSDLProcessor.java b/src/main/java/org/wso2/soaptorest/WSDLProcessor.java
index b15794d..84a45ef 100644
--- a/src/main/java/org/wso2/soaptorest/WSDLProcessor.java
+++ b/src/main/java/org/wso2/soaptorest/WSDLProcessor.java
@@ -211,6 +211,9 @@ private XSElement processXmlSchemaElement(XmlSchemaElement xmlSchemaElement) {
} else {
log.warn("Data type for the child element " + xmlSchemaElement.getName() + "did " + "not processed");
}
+ if (xmlSchemaElement.getMinOccurs() == 0) {
+ xsElement.setOptional(true);
+ }
return xsElement;
}
diff --git a/src/main/java/org/wso2/soaptorest/models/SOAPtoRESTConversionData.java b/src/main/java/org/wso2/soaptorest/models/SOAPtoRESTConversionData.java
index efe470e..e832cc9 100644
--- a/src/main/java/org/wso2/soaptorest/models/SOAPtoRESTConversionData.java
+++ b/src/main/java/org/wso2/soaptorest/models/SOAPtoRESTConversionData.java
@@ -62,4 +62,8 @@ public String getSoapPort() {
return soapPort;
}
+
+ public OpenAPI getOpenAPI() {
+ return openAPI;
+ }
}
diff --git a/src/main/java/org/wso2/soaptorest/models/XSElement.java b/src/main/java/org/wso2/soaptorest/models/XSElement.java
index 69b1d52..9032edb 100644
--- a/src/main/java/org/wso2/soaptorest/models/XSElement.java
+++ b/src/main/java/org/wso2/soaptorest/models/XSElement.java
@@ -27,6 +27,7 @@ public class XSElement {
QName name;
QName type;
QName refKey;
+ boolean isOptional;
XSDataType inlineComplexType;
boolean isArray;
@@ -77,4 +78,12 @@ public boolean isArray() {
public void setArray(boolean array) {
isArray = array;
}
+
+ public boolean isOptional() {
+ return isOptional;
+ }
+
+ public void setOptional(boolean optional) {
+ isOptional = optional;
+ }
}
diff --git a/src/main/java/org/wso2/soaptorest/utils/ListJSONPaths.java b/src/main/java/org/wso2/soaptorest/utils/ListJSONPaths.java
index c162fe0..43ccaa0 100644
--- a/src/main/java/org/wso2/soaptorest/utils/ListJSONPaths.java
+++ b/src/main/java/org/wso2/soaptorest/utils/ListJSONPaths.java
@@ -17,6 +17,7 @@
*/
package org.wso2.soaptorest.utils;
+import io.swagger.v3.oas.models.OpenAPI;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
import org.json.JSONObject;
@@ -96,11 +97,10 @@ private static void readArray(JSONArray array, String jsonPath, ArrayList getJsonPathsFromExample(Example example) {
-
+ public static ArrayList getJsonPathsFromExample(Example example, Map jsonPathSchemaMapping) {
ArrayList pathList = new ArrayList<>();
if (example != null) {
- listExamples("", example, pathList);
+ listExamples("", example, pathList, jsonPathSchemaMapping);
}
return pathList;
}
@@ -112,14 +112,18 @@ public static ArrayList getJsonPathsFromExample(Example example) {
* @param parameterJsonPathMapping list of json paths
* @return the arraylist of the available json paths
*/
- public static void listExamples(String parent, Example example, ArrayList parameterJsonPathMapping) {
+ public static void listExamples(String parent, Example example, ArrayList parameterJsonPathMapping
+ , Map jsonPathSchemaMapping) {
if (example != null) {
if (SOAPToRESTConstants.OBJECT_TYPE.equals(example.getTypeName())) {
Map values = ((ObjectExample) example).getValues();
if (values != null) {
for (Map.Entry entry : values.entrySet()) {
String childKey = parent.isEmpty() ? entry.getKey() : parent + "." + entry.getKey();
- listExamples(childKey, entry.getValue(), parameterJsonPathMapping);
+ if (entry.getValue() != null && entry.getValue().getName() != null) {
+ jsonPathSchemaMapping.put(entry.getKey(), entry.getValue().getName());
+ }
+ listExamples(childKey, entry.getValue(), parameterJsonPathMapping, jsonPathSchemaMapping);
}
} else if (StringUtils.isNotBlank(parent)) {
parameterJsonPathMapping.add(parent);
@@ -132,9 +136,9 @@ public static void listExamples(String parent, Example example, ArrayList
-
+
-
+
diff --git a/src/test/resources/complex/nested.xsd b/src/test/resources/complex/nested.xsd
index 6c53484..d33fadc 100644
--- a/src/test/resources/complex/nested.xsd
+++ b/src/test/resources/complex/nested.xsd
@@ -2,17 +2,21 @@
-
+
-
+
-
+
-
+
+
+
+
+
@@ -24,13 +28,14 @@
+
-
+