- 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[JSON Message with base64encoded XML content] stylesheet(Liquid Stylesheet)--> |Transform| xml["XML"] Json --> stylesheet

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=\"ACME\" ExportType=\"FULL\">\n   <ENTITY ACTIVEDATE=\"\"\n BANKACCOUNTID=\"\"\n VENDORGROUPID=\"\"\n VENDORORGANIZATIONNAME=\"\"/>\n</Document>\n"
    }
}

Next Step