diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/templating/AbstractTemplateInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/templating/AbstractTemplateInterceptor.java
index 040078f7b4..dc9d46b1e7 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/templating/AbstractTemplateInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/templating/AbstractTemplateInterceptor.java
@@ -31,7 +31,7 @@
import static com.predic8.membrane.core.interceptor.Outcome.ABORT;
import static com.predic8.membrane.core.interceptor.Outcome.*;
import static com.predic8.membrane.core.resolver.ResolverMap.*;
-import static com.predic8.membrane.core.util.StringUtil.*;
+import static com.predic8.membrane.core.util.text.StringUtil.*;
import static java.nio.charset.StandardCharsets.*;
import static org.apache.commons.text.StringEscapeUtils.*;
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/templating/TemplateInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/templating/TemplateInterceptor.java
index 266b586ad3..74fa39742d 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/templating/TemplateInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/templating/TemplateInterceptor.java
@@ -31,7 +31,7 @@
import static com.predic8.membrane.core.interceptor.Outcome.*;
import static com.predic8.membrane.core.lang.ScriptingUtils.*;
import static com.predic8.membrane.core.util.FileUtil.*;
-import static com.predic8.membrane.core.util.StringUtil.addLineNumbers;
+import static com.predic8.membrane.core.util.text.StringUtil.addLineNumbers;
import static java.nio.charset.StandardCharsets.*;
/**
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/xml/Json2XmlInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/xml/Json2XmlInterceptor.java
index 9f633bfb28..7337dd3ff4 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/xml/Json2XmlInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/xml/Json2XmlInterceptor.java
@@ -24,7 +24,7 @@
import static com.predic8.membrane.core.http.MimeType.*;
import static com.predic8.membrane.core.interceptor.Interceptor.Flow.*;
import static com.predic8.membrane.core.interceptor.Outcome.*;
-import static com.predic8.membrane.core.util.StringUtil.*;
+import static com.predic8.membrane.core.util.text.StringUtil.*;
import static java.nio.charset.StandardCharsets.*;
/**
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/xslt/XSLTInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/xslt/XSLTInterceptor.java
index 8eb193cd95..d45250c32b 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/xslt/XSLTInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/xslt/XSLTInterceptor.java
@@ -19,111 +19,127 @@
import com.predic8.membrane.core.interceptor.*;
import com.predic8.membrane.core.multipart.*;
import com.predic8.membrane.core.util.*;
-import com.predic8.membrane.core.util.text.*;
+import org.jetbrains.annotations.*;
import org.slf4j.*;
+import javax.xml.transform.*;
import javax.xml.transform.stream.*;
import java.util.*;
import static com.predic8.membrane.core.exceptions.ProblemDetails.*;
+import static com.predic8.membrane.core.interceptor.Interceptor.Flow.*;
import static com.predic8.membrane.core.interceptor.Outcome.*;
+import static com.predic8.membrane.core.interceptor.Outcome.ABORT;
+import static com.predic8.membrane.core.util.text.StringUtil.*;
+import static com.predic8.membrane.core.util.text.TextUtil.*;
/**
* @description
- * The transform feature applies an XSLT transformation to the content in the body of a message. After the
- * transformation the body content is replaced with the result of the transformation.
- *
+ * The transform feature applies an XSLT transformation to the content in the body of a message. After the
+ * transformation the body content is replaced with the result of the transformation.
+ *
* @topic 2. Enterprise Integration Patterns
*/
-@MCElement(name="transform")
+@MCElement(name = "transform")
public class XSLTInterceptor extends AbstractInterceptor {
- private static final Logger log = LoggerFactory.getLogger(XSLTInterceptor.class.getName());
+ private static final Logger log = LoggerFactory.getLogger(XSLTInterceptor.class.getName());
- private String xslt;
- private volatile XSLTTransformer xsltTransformer;
- private final XOPReconstitutor xopr = new XOPReconstitutor();
+ private String xslt;
+ private volatile XSLTTransformer xsltTransformer;
+ private final XOPReconstitutor xopr = new XOPReconstitutor();
- public XSLTInterceptor() {
- name = "xslt transformer";
- }
+ public XSLTInterceptor() {
+ name = "xslt transformer";
+ }
- @Override
- public Outcome handleRequest(Exchange exc) {
- try {
- transformMsg(exc.getRequest(), xslt, exc.getStringProperties());
- } catch (Exception e) {
- user(router.getConfiguration().isProduction(),getDisplayName())
- .detail("Error transforming request!")
- .exception(e)
- .buildAndSetResponse(exc);
- return ABORT;
- }
- return CONTINUE;
- }
+ @Override
+ public Outcome handleRequest(Exchange exc) {
+ return handleInternal(exc, REQUEST);
+ }
+
+ @Override
+ public Outcome handleResponse(Exchange exc) {
+ return handleInternal(exc, RESPONSE);
+ }
+
+ private Outcome handleInternal(Exchange exc, Flow flow) {
+ var msg = exc.getMessage(flow);
- @Override
- public Outcome handleResponse(Exchange exc) {
try {
- transformMsg(exc.getResponse(), xslt, exc.getStringProperties());
+ transformMsg(msg, exc.getStringProperties());
+ } catch (TransformerException e) {
+ log.debug("", e);
+ if (e.getMessage() != null && e.getMessage().contains("not allowed in prolog")) {
+ user(router.getConfiguration().isProduction(), getDisplayName())
+ .title("Content not allowed in prolog of XML input.")
+ .detail("Check for extra characters before the XML declaration ")
+ .internal("offendingInput", truncateAfter(msg.getBodyAsStringDecoded() + "...", 50))
+ .buildAndSetResponse(exc);
+ return ABORT;
+ }
+ return createErrorResponse(exc,e,flow);
} catch (Exception e) {
- log.error("Error transforming response!", e);
- user(router.getConfiguration().isProduction(),getDisplayName())
- .detail("Error transforming response!")
- .exception(e)
- .buildAndSetResponse(exc);
- return ABORT;
+ log.info("", e);
+ return createErrorResponse(exc,e,flow);
}
return CONTINUE;
- }
-
- private void transformMsg(Message msg, String ss, Map parameter) throws Exception {
- if (msg.isBodyEmpty())
- return;
- msg.setBodyContent(xsltTransformer.transform(
- new StreamSource(xopr.reconstituteIfNecessary(msg)), parameter));
- }
-
- @Override
- public void init() {
- super.init();
+ }
+
+ private @NotNull Outcome createErrorResponse(Exchange exc, Exception e, Flow flow) {
+ user(router.getConfiguration().isProduction(), getDisplayName())
+ .detail("Error transforming message!")
+ .exception(e)
+ .internal("flow", flow.toString())
+ .buildAndSetResponse(exc);
+ return ABORT;
+ }
+
+ private void transformMsg(Message msg, Map parameter) throws Exception {
+ if (msg.isBodyEmpty())
+ return;
+ msg.setBodyContent(xsltTransformer.transform(
+ new StreamSource(xopr.reconstituteIfNecessary(msg)), parameter));
+ }
+
+ @Override
+ public void init() {
+ super.init();
try {
xsltTransformer = new XSLTTransformer(xslt, router, getConcurrency());
} catch (Exception e) {
- log.debug("",e);
+ log.debug("",e);
throw new ConfigurationException("Could not create XSLT transformer",e);
+
}
}
- private static int getConcurrency() {
- return Runtime.getRuntime().availableProcessors() * 2;
- }
-
- public String getXslt() {
- return xslt;
- }
-
- /**
- * @description Location of the XSLT stylesheet that will be applied to request and response.
- * @example strip.xslt
- */
- @MCAttribute
- public void setXslt(String xslt) {
- this.xslt = xslt;
- this.xsltTransformer = null;
- }
-
- @Override
- public String getShortDescription() {
- return "Applies an XSLT transformation.";
- }
-
- @Override
- public String getLongDescription() {
- return TextUtil.removeFinalChar(getShortDescription()) +
- " using the stylesheet at " +
- TextUtil.linkURL(xslt) +
- " .";
- }
+ private static int getConcurrency() {
+ return Runtime.getRuntime().availableProcessors() * 2;
+ }
+
+ public String getXslt() {
+ return xslt;
+ }
+
+ /**
+ * @description Location of the XSLT stylesheet that will be applied to request and response.
+ * @example strip.xslt
+ */
+ @MCAttribute
+ public void setXslt(String xslt) {
+ this.xslt = xslt;
+ this.xsltTransformer = null;
+ }
+
+ @Override
+ public String getShortDescription() {
+ return "Applies an XSLT transformation.";
+ }
+
+ @Override
+ public String getLongDescription() {
+ return "%s using the stylesheet at %s .".formatted(removeFinalChar(getShortDescription()), linkURL(xslt));
+ }
}
diff --git a/core/src/main/java/com/predic8/membrane/core/lang/jsonpath/JsonpathExchangeExpression.java b/core/src/main/java/com/predic8/membrane/core/lang/jsonpath/JsonpathExchangeExpression.java
index 1f89704390..a4d22a1fcf 100644
--- a/core/src/main/java/com/predic8/membrane/core/lang/jsonpath/JsonpathExchangeExpression.java
+++ b/core/src/main/java/com/predic8/membrane/core/lang/jsonpath/JsonpathExchangeExpression.java
@@ -28,7 +28,7 @@
import java.io.*;
import java.util.*;
-import static com.predic8.membrane.core.util.StringUtil.*;
+import static com.predic8.membrane.core.util.text.StringUtil.*;
import static java.lang.Boolean.*;
import static java.nio.charset.StandardCharsets.*;
diff --git a/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxy.java b/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxy.java
index d8384ad590..e2e060ce56 100644
--- a/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxy.java
+++ b/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxy.java
@@ -38,7 +38,7 @@
import static com.predic8.membrane.core.lang.ExchangeExpression.Language.SPEL;
import static com.predic8.membrane.core.lang.ExchangeExpression.expression;
-import static com.predic8.membrane.core.util.StringUtil.maskNonPrintableCharacters;
+import static com.predic8.membrane.core.util.text.StringUtil.maskNonPrintableCharacters;
/**
* @description The api proxy extends the serviceProxy with API related functions like OpenAPI support and path parameters.
diff --git a/core/src/main/java/com/predic8/membrane/core/transport/http/HttpServerHandler.java b/core/src/main/java/com/predic8/membrane/core/transport/http/HttpServerHandler.java
index b12139d834..63cac2f309 100644
--- a/core/src/main/java/com/predic8/membrane/core/transport/http/HttpServerHandler.java
+++ b/core/src/main/java/com/predic8/membrane/core/transport/http/HttpServerHandler.java
@@ -35,8 +35,8 @@
import static com.predic8.membrane.core.transport.http.ByteStreamLogging.wrapConnectionOutputStream;
import static com.predic8.membrane.core.transport.http.HttpServerHandler.RequestProcessingResult.*;
import static com.predic8.membrane.core.transport.http.HttpServerThreadFactory.DEFAULT_THREAD_NAME;
-import static com.predic8.membrane.core.util.StringUtil.maskNonPrintableCharacters;
-import static com.predic8.membrane.core.util.StringUtil.truncateAfter;
+import static com.predic8.membrane.core.util.text.StringUtil.maskNonPrintableCharacters;
+import static com.predic8.membrane.core.util.text.StringUtil.truncateAfter;
import static java.lang.Thread.currentThread;
public class HttpServerHandler extends AbstractHttpHandler implements Runnable, TwoWayStreaming {
diff --git a/core/src/main/java/com/predic8/membrane/core/util/text/StringUtil.java b/core/src/main/java/com/predic8/membrane/core/util/text/StringUtil.java
index 326a10a9d6..3b80e88a9b 100644
--- a/core/src/main/java/com/predic8/membrane/core/util/text/StringUtil.java
+++ b/core/src/main/java/com/predic8/membrane/core/util/text/StringUtil.java
@@ -12,7 +12,7 @@
See the License for the specific language governing permissions and
limitations under the License. */
-package com.predic8.membrane.core.util;
+package com.predic8.membrane.core.util.text;
import java.util.*;
diff --git a/core/src/test/java/com/predic8/membrane/core/interceptor/xslt/XSLTInterceptorTest.java b/core/src/test/java/com/predic8/membrane/core/interceptor/xslt/XSLTInterceptorTest.java
index 32011d695f..29f2c1772e 100644
--- a/core/src/test/java/com/predic8/membrane/core/interceptor/xslt/XSLTInterceptorTest.java
+++ b/core/src/test/java/com/predic8/membrane/core/interceptor/xslt/XSLTInterceptorTest.java
@@ -13,79 +13,84 @@
limitations under the License. */
package com.predic8.membrane.core.interceptor.xslt;
-import java.io.InputStream;
-
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathExpressionException;
-import javax.xml.xpath.XPathFactory;
-
-import org.junit.jupiter.api.Test;
-import org.xml.sax.InputSource;
-
-import com.predic8.membrane.core.router.DummyTestRouter;
-import com.predic8.membrane.core.exchange.Exchange;
-import com.predic8.membrane.core.http.Response;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import com.predic8.membrane.core.exchange.*;
+import com.predic8.membrane.core.http.*;
+import com.predic8.membrane.core.interceptor.*;
+import com.predic8.membrane.core.router.*;
+import org.hamcrest.*;
+import org.junit.jupiter.api.*;
+import org.xml.sax.*;
+
+import javax.xml.xpath.*;
+import java.io.*;
+import java.net.*;
+
+import static com.predic8.membrane.core.http.Request.get;
+import static com.predic8.membrane.core.http.Response.*;
+import static com.predic8.membrane.core.interceptor.Outcome.ABORT;
+import static org.junit.jupiter.api.Assertions.*;
public class XSLTInterceptorTest {
- Exchange exc = new Exchange(null);
- final XPath xpath = XPathFactory.newInstance().newXPath();
-
- @Test
- public void testRequest() throws Exception {
- exc = new Exchange(null);
- exc.setResponse(Response.ok().body(getClass().getResourceAsStream("/customer.xml"), true).build());
-
- XSLTInterceptor i = new XSLTInterceptor();
- i.setXslt("classpath:/customer2person.xsl");
- i.init(new DummyTestRouter());
- i.handleResponse(exc);
-
- //printBodyContent();
- assertXPath("/person/name/first", "Rick");
- assertXPath("/person/name/last", "Cort\u00e9s Ribotta");
- assertXPath("/person/address/street",
- "Calle P\u00fablica \"B\" 5240 Casa 121");
- assertXPath("/person/address/city", "Omaha");
- }
-
- @Test
- public void testXSLTParameter() throws Exception {
- exc = new Exchange(null);
- exc.setResponse(Response.ok().body(getClass().getResourceAsStream("/customer.xml"), true).build());
-
- exc.setProperty("XSLT_COMPANY", "predic8");
-
- XSLTInterceptor i = new XSLTInterceptor();
- i.setXslt("classpath:/customer2personAddCompany.xsl");
- i.init(new DummyTestRouter());
- i.handleResponse(exc);
-
- //printBodyContent();
- assertXPath("/person/name/first", "Rick");
- assertXPath("/person/name/last", "Cort\u00e9s Ribotta");
- assertXPath("/person/address/street",
- "Calle P\u00fablica \"B\" 5240 Casa 121");
- assertXPath("/person/address/city", "Omaha");
- assertXPath("/person/company", "predic8");
- }
-
- @SuppressWarnings("unused")
- private void printBodyContent() throws Exception {
- InputStream i = exc.getResponse().getBodyAsStream();
- int read = 0;
- byte[] buf = new byte[4096];
- while ((read = i.read(buf)) != -1) {
- System.out.write(buf, 0, read);
- }
- }
-
- private void assertXPath(String xpathExpr, String expected)
- throws XPathExpressionException {
- assertEquals(expected, xpath.evaluate(xpathExpr, new InputSource(exc
- .getResponse().getBodyAsStream())));
- }
-
+ Exchange exc = new Exchange(null);
+ final XPath xpath = XPathFactory.newInstance().newXPath();
+
+ @Test
+ void testRequest() throws Exception {
+ exc = new Exchange(null);
+ exc.setResponse(ok().body(getClass().getResourceAsStream("/customer.xml"), true).build());
+
+ XSLTInterceptor i = new XSLTInterceptor();
+ i.setXslt("classpath:/customer2person.xsl");
+ i.init(new DummyTestRouter());
+ i.handleResponse(exc);
+
+ //printBodyContent();
+ assertXPath("/person/name/first", "Rick");
+ assertXPath("/person/name/last", "Cort\u00e9s Ribotta");
+ assertXPath("/person/address/street",
+ "Calle P\u00fablica \"B\" 5240 Casa 121");
+ assertXPath("/person/address/city", "Omaha");
+ }
+
+ @Test
+ void testXSLTParameter() throws Exception {
+ exc = new Exchange(null);
+ exc.setResponse(ok().body(getClass().getResourceAsStream("/customer.xml"), true).build());
+
+ exc.setProperty("XSLT_COMPANY", "predic8");
+
+ XSLTInterceptor i = new XSLTInterceptor();
+ i.setXslt("classpath:/customer2personAddCompany.xsl");
+ i.init(new DummyTestRouter());
+ i.handleResponse(exc);
+
+ //printBodyContent();
+ assertXPath("/person/name/first", "Rick");
+ assertXPath("/person/name/last", "Cort\u00e9s Ribotta");
+ assertXPath("/person/address/street",
+ "Calle P\u00fablica \"B\" 5240 Casa 121");
+ assertXPath("/person/address/city", "Omaha");
+ assertXPath("/person/company", "predic8");
+ }
+
+ @Test
+ void noContentInProlog() throws Exception {
+ exc = get("http://localhost/").body("rubbish").buildExchange();
+
+ var i = new XSLTInterceptor();
+ i.setXslt("classpath:/customer2personAddCompany.xsl");
+ i.init(new DummyTestRouter());
+ assertEquals(ABORT, i.handleRequest(exc));
+ assertEquals(400, exc.getResponse().getStatusCode());
+ String body = exc.getResponse().getBodyAsStringDecoded();
+ assertTrue(body.contains("rubbish"));
+ assertTrue(body.contains("not allowed in prolog"));
+ }
+
+ private void assertXPath(String xpathExpr, String expected)
+ throws XPathExpressionException {
+ assertEquals(expected, xpath.evaluate(xpathExpr, new InputSource(exc
+ .getResponse().getBodyAsStream())));
+ }
}
diff --git a/core/src/test/java/com/predic8/membrane/core/util/StringUtilTest.java b/core/src/test/java/com/predic8/membrane/core/util/StringUtilTest.java
index bad24a0096..a39b1ca724 100644
--- a/core/src/test/java/com/predic8/membrane/core/util/StringUtilTest.java
+++ b/core/src/test/java/com/predic8/membrane/core/util/StringUtilTest.java
@@ -18,8 +18,7 @@
import java.util.*;
-import static com.predic8.membrane.core.util.StringUtil.*;
-import static com.predic8.membrane.core.util.StringUtil.splitByComma;
+import static com.predic8.membrane.core.util.text.StringUtil.*;
import static org.junit.jupiter.api.Assertions.*;
class StringUtilTest {
diff --git a/distribution/src/test/java/com/predic8/membrane/tutorials/xml/XsltTransformationTutorialTest.java b/distribution/src/test/java/com/predic8/membrane/tutorials/xml/XsltTransformationTutorialTest.java
index e7d36b7022..300fdc5285 100644
--- a/distribution/src/test/java/com/predic8/membrane/tutorials/xml/XsltTransformationTutorialTest.java
+++ b/distribution/src/test/java/com/predic8/membrane/tutorials/xml/XsltTransformationTutorialTest.java
@@ -25,7 +25,7 @@
public class XsltTransformationTutorialTest extends AbstractXmlTutorialTest{
@Override
protected String getTutorialYaml() {
- return "40-XSLT-transformation.yaml";
+ return "40-XSLT-Transformation-group.yaml";
}
@Test
diff --git a/distribution/src/test/java/com/predic8/membrane/tutorials/xml/XsltXML2JSONTransformationTutorialTest.java b/distribution/src/test/java/com/predic8/membrane/tutorials/xml/XsltXML2JSONTransformationTutorialTest.java
new file mode 100644
index 0000000000..9bfcc3fbba
--- /dev/null
+++ b/distribution/src/test/java/com/predic8/membrane/tutorials/xml/XsltXML2JSONTransformationTutorialTest.java
@@ -0,0 +1,32 @@
+package com.predic8.membrane.tutorials.xml;
+
+import org.junit.jupiter.api.*;
+
+import java.io.*;
+
+import static io.restassured.RestAssured.*;
+import static io.restassured.http.ContentType.*;
+import static org.hamcrest.Matchers.*;
+
+public class XsltXML2JSONTransformationTutorialTest extends AbstractXmlTutorialTest{
+ @Override
+ protected String getTutorialYaml() {
+ return "35-XSLT-Transformation-to-json.yaml";
+ }
+
+ @Test
+ void xsltTransformsXml() throws IOException {
+ // @formatter:off
+ given()
+ .body(readFileFromBaseDir("books.xml"))
+ .contentType(XML)
+ .when()
+ .post("http://localhost:2000")
+ .then()
+ .statusCode(200)
+ .contentType(JSON)
+ .body("books.size()", greaterThan(0))
+ .body("books[0].year", equalTo("1975"));
+ // @formatter:on
+ }
+}
diff --git a/distribution/tutorials/xml/35-XSLT-Transformation-to-json.yaml b/distribution/tutorials/xml/35-XSLT-Transformation-to-json.yaml
new file mode 100644
index 0000000000..54e47b4e03
--- /dev/null
+++ b/distribution/tutorials/xml/35-XSLT-Transformation-to-json.yaml
@@ -0,0 +1,26 @@
+# yaml-language-server: $schema=https://www.membrane-api.io/v7.0.6.json
+#
+# Tutorial: XSLT Transformation from XML to JSON
+#
+# The `transform` plugin uses a stylesheet to transform XML into a
+# different text format. One common use case is to transform XML into JSON.
+# The templates in the `to-json.xsl` stylesheet could be used as a starting point
+# for own XML to JSON transformations.
+#
+# Try:
+# curl -d @books.xml -H "Content-Type: text/xml" localhost:2000
+
+api:
+ port: 2000
+ flow:
+ - request:
+ - transform:
+ # The stylesheet to use for the transformation
+ xslt: to-json.xsl
+ - setHeader:
+ name: Content-Type
+ value: application/json
+ # The beautifier needs the Content-Type to format the output as JSON
+ - beautifier: {}
+ - return:
+ status: 200
diff --git a/distribution/tutorials/xml/40-XSLT-transformation.yaml b/distribution/tutorials/xml/40-XSLT-Transformation-group.yaml
similarity index 58%
rename from distribution/tutorials/xml/40-XSLT-transformation.yaml
rename to distribution/tutorials/xml/40-XSLT-Transformation-group.yaml
index eed4e74c06..8d0e823ee4 100644
--- a/distribution/tutorials/xml/40-XSLT-transformation.yaml
+++ b/distribution/tutorials/xml/40-XSLT-Transformation-group.yaml
@@ -1,11 +1,11 @@
# yaml-language-server: $schema=https://www.membrane-api.io/v7.0.6.json
#
-# Tutorial: XSLT Transformation
+# Tutorial: XSLT Transformation Grouping
#
-# Use an XSLT stylesheet to transform XML.
+# Complex XSLT transformation that regroups input
#
# Try:
-# curl -d @books.xml localhost:2000
+# curl -d @books.xml -H "Content-Type: text/xml" localhost:2000
api:
port: 2000
diff --git a/distribution/tutorials/xml/to-json.xsl b/distribution/tutorials/xml/to-json.xsl
new file mode 100644
index 0000000000..156fe22242
--- /dev/null
+++ b/distribution/tutorials/xml/to-json.xsl
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+ { "books": [
+
+ ] }
+
+
+
+
+ {
+
+ }
+
+ ,
+
+
+
+
+ "": ""
+ ,
+
+
+
+
\ No newline at end of file
diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md
index 468b838e86..16300a9621 100644
--- a/docs/ROADMAP.md
+++ b/docs/ROADMAP.md
@@ -8,6 +8,9 @@
PRIO 1:
+- Reverse:
+ - First parse
+ - Second validate YAML
- Tutorials:
- Add how to run the tutorials in a Docker container
- HotReload for YAML
@@ -17,7 +20,7 @@ PRIO 1:
- if: Add hint in documentation: use choice otherwise for else TB
- Register JSON Schema for YAML at: https://www.schemastore.org TB
- create test asserting that connection reuse via proxy works TP
-- Central description of Membrane Languages, Cheat Sheets, links to their docs.
+- Central description of Membrane Languages, Cheat Sheets, links to their docs. TP
- Central description of MEMBRANE_* environment variables
- Like MEMBRANE_HOME...
- @coderabbitai look through the code base for usages of these variables and suggest documentation