- 6 minutes to read

Base64-decode XML from JSON with a Liquid Stylesheet

Decode base64-encoded XML embedded in JSON messages and render it as readable XML in Nodinite Log Views. This Liquid-based Stylesheet enables operators to preview, inspect, and download XML content directly—no additional tooling required.

Benefits:

  • ✅ View embedded XML inline without external tools
  • ✅ Decode base64 client-side for fast previews and diagnostics
  • ✅ Export rendered XML for sharing or offline analysis
graph LR Json["fa:fa-file-code JSON Message
with base64-encoded XML"] --> stylesheet["fa:fa-droplet Liquid Stylesheet
with JavaScript"] stylesheet -->|"Transform"| xml["fa:fa-file-lines Readable XML
in the Log View"] style Json fill:#2678BD,color:#fff style stylesheet fill:#E47528,color:#fff style xml fill:#93d16d,color:#000

The JSON input (blue) passes the Liquid Stylesheet (orange) and appears as readable XML (green). The StylesheetScriptsEnabled System Parameter must be true for the JavaScript to run.

This example demonstrates how to transform a JSON message containing base64-encoded XML into rendered XML output using a Liquid Stylesheet. Use this approach to make data accessible to end-users while optionally removing sensitive information.

1. Input JSON

The following data is an example of a JSON message with a base64 encoded content:

{
	"method": "post",
	"queries": {
		"systemProperties": "Run Details"
	},
	"path": "/orders/messages",
	"host": {
		"connection": {
			"name": "/subscriptions/ae3da538-3e2b-4490-9feb-7dfe30a6683c/resourceGroups/Default-Storage-NorthEurope/providers/Microsoft.Web/connections/servicebus"
		}
	},
	"body": {
		"ContentData": "PG5zMDpPcmRlcnMgeG1sbnM6bnMwPSJTdXBwbHlDaGFpbi5TY2hlbWFzLzEuMCI+CiAgPE9yZGVyPgogICAgPElkPjEzMzc8L0lkPgogICAgPEFtb3VudD4xMzM3PC9BbW91bnQ+CiAgICA8Q2l0eT5Ta2VsbGVmdGXDpTwvQ2l0eT4KICA8L09yZGVyPgo8L25zMDpPcmRlcnM+",
		"CorrelationId": "BOWC123",
		"Properties": {
			"ExtendedProperties/1.0#SystemInterchangeId": "BOWC123"
		}
	}
}

2. Liquid Stylesheet

Use a Liquid Stylesheet to decode and render the base64-encoded XML content.

Warning

Script Execution Requirement: This stylesheet contains embedded JavaScript code. The StylesheetScriptsEnabled system parameter must be set to true for this stylesheet to execute. Only members of the built-in Administrators role can enable this parameter. See StylesheetScriptsEnabled for security considerations.

Important

Configuration Required: Enable "Message Body is Json" in the Stylesheet settings. When enabled, Nodinite parses the JSON message and injects properties as Liquid variables, allowing you to access nested properties like {{body['ContentData']}} directly in your Liquid template.

	<textarea rows="50" cols="50" style="border: none;outline: none;" id="demo"> </textarea>
	<pre id="demoprint" style="margin:0; padding:0; border:none; display:none;"></pre>
	<script>
		var editor;
		function codemirroreditor(elem) {
			var mime = 'application/xml';
			 editor = CodeMirror.fromTextArea(elem, {
				lineNumbers: true,
				mode: mime,
				indentWithTabs: true,
				smartIndent: true,
				lineNumbers: true,
				matchBrackets: true,
				lineWrapping: true,
				autofocus: true,
				autoRefresh: true,
				extraKeys: { "Ctrl-Space": "autocomplete" }
			});
		}

		function b64DecodeUnicode(str) {
			return decodeURIComponent(
				atob(str)
					.split('')
					.map(function(c) {
						return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
					})
					.join('')
				);
		}


		var content = b64DecodeUnicode("{{body['ContentData']}}");
		function displayXml() {
			var elem = document.getElementById('demo');
			elem.value = content;
			codemirroreditor(elem);
		};

		function htmlEntities(str) {
			var htmlString = String(str).replace(/&/g, '&amp;').replace(/</g,'&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
			return htmlString;
		}

		window.addEventListener('beforeprint', (event) => {
			$(editor.getWrapperElement()).hide();
			$("#demoprint").html(htmlEntities(content)).show();
		});

		window.addEventListener('afterprint', (event) => {
			$(editor.getWrapperElement()).show();
			$("#demoprint").hide();
		});

		displayXml();
		</script>

<style>
  html {height:calc(100vh - 200px); min-height:500px;}
  body {height:100%;}
  .CodeMirror {
    min-height: 200px;
    height: 100%;
  }
</style>

3. XML Output

Applying the Liquid Stylesheet to the Input JSON produces the following XML output:

<ns0:Orders xmlns:ns0="SupplyChain.Schemas/1.0">
  <Order>
    <Id>1337</Id>
    <Amount>1337</Amount>
    <City>Skellefteå</City>
  </Order>
</ns0:Orders>

Note

This example performs Unicode translation to properly handle special characters like "å" in the City field.

4. Download Button

Add a download button to allow users to save the rendered XML content locally.

Warning

Script Execution Requirement: This enhanced stylesheet contains embedded JavaScript code. The StylesheetScriptsEnabled system parameter must be set to true for this stylesheet to execute. See StylesheetScriptsEnabled for security considerations.

Instructions: Adjust the CONTENTTYPE and FILENAME variables in the code below to match your expected file type.

<button class="btn btn-success" id="downloadButton">Download</button>
<style>

  .btn {
      display: inline-block;
      font-weight: 400;
      color: #212529;
      text-align: center;
      vertical-align: middle;
      user-select: none;
      background-color: transparent;
      border: 1px solid transparent;
      padding: 0.375rem 0.75rem;
      font-size: 1rem;
      line-height: 1.5;
      border-radius: 0.25rem;
      transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
      box-sizing: border-box;
      margin: 1rem;
  }    

  .btn-success {
      color: #fff;
      background-color: #68a19b;
      border-color: #68a19b;
      box-shadow: inset 0 1px 0 rgb(255 255 255 / 15%), 0 1px 1px rgb(0 0 0 / 8%);
  }
  html {height:calc(100vh - 200px); min-height:400px;}
  body {height:100%;}
  .CodeMirror {
    min-height: 200px;
    height: calc(100%-40px);
  }
</style>

<textarea rows="50" cols="50" id="demo"></textarea>
<pre id="demoprint" style="margin:0; padding:0; border:none; display:none;"></pre>
<script>
(function() {
  ///// CHANGE THIS
  var CONTENTTYPE = "application/xml";
  var FILENAME = "out.xml";
  ////////////////


    var editor;
    function codemirroreditor(id) {
      var mime = 'application/xml';
      editor = CodeMirror.fromTextArea(
        document.getElementById(id), 
          {
                lineNumbers: true,
                mode: mime,
                indentWithTabs: true,
                smartIndent: true,
                lineNumbers: true,
                matchBrackets: true,
                lineWrapping: true,
                autofocus: true,
                autoRefresh: true,
                extraKeys: { "Ctrl-Space": "autocomplete" }
          }
        );
  }

  function b64DecodeUnicode(str) {
        return decodeURIComponent(atob(str).split('').map(function(c) {
            return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
        }).join(''));
  }
    var content = b64DecodeUnicode("{{ body['ContentData'] }}");
  function displayXml() {
    $("#demo").html(content);
    codemirroreditor('demo');
  };

    function htmlEntities(str) {
    var htmlString = String(str).replace(/&/g, '&amp;').replace(/</g,'&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
    return htmlString;
  }

    window.addEventListener('beforeprint', (event) => {
      $(editor.getWrapperElement()).hide();
        $("#demoprint").html(htmlEntities(content)).show();
    $("#downloadButton").hide();
    });

    window.addEventListener('afterprint', (event) => {
        $(editor.getWrapperElement()).show();
        $("#demoprint").hide();
    $("#downloadButton").show();
    });
  displayXml();

  function save(filename, data) {
    const blob = new Blob([data], {type: CONTENTTYPE});
    if(window.navigator.msSaveOrOpenBlob) {
      window.navigator.msSaveBlob(blob, filename);
    }
    else{
      const elem = window.document.createElement('a');
      elem.href = window.URL.createObjectURL(blob);
      elem.download = filename;        
      document.body.appendChild(elem);
      elem.click();        
      document.body.removeChild(elem);
    }
  }

  $("#downloadButton").click(() => { save(FILENAME, content) } ) 

})();
</script>

Escaped XML

If your content is not base64-encoded and contains quotes ("), extract the content using template literals:

var content = `{{body.content}}`;

Escaped XML Example

{
    "body": {
        "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Document Company=\"CONTOSO\\\" ExportType=\"FULL\">\n   <ENTITY ACTIVEDATE=\"\"\n BANKACCOUNTID=\"\"\n VENDORGROUPID=\"\"\n VENDORORGANIZATIONNAME=\"\"/>\n</Document>\n"
    }
}

Next Steps