diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/JSTree.php b/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/JSTree.php
index 99394d18fc306dbbe7ecba0f471ca60b847c669f..eec0d74e2a304a18c3dee2bd85dfb6c5e2f003cd 100644
--- a/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/JSTree.php
+++ b/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/JSTree.php
@@ -120,10 +120,15 @@ class JSTree extends AbstractBase
     {
         if (!empty($context) && !empty($mode)) {
             if ($mode == 'List') {
-                return $this->jsonToHTML(
-                    json_decode($this->getJSON($hierarchyID, $context)),
-                    $this->recordDriver->getUniqueId()
-                );
+                $json = $this->getDataSource()->getJSON($hierarchyID);
+                if (!empty($json)) {
+                    return $this->jsonToHTML(
+                        json_decode($json),
+                        $context,
+                        $hierarchyID,
+                        $this->recordDriver->getUniqueId()
+                    );
+                }
             } else {
                 return $this->transformCollectionXML(
                     $context, $mode, $hierarchyID, $recordID
@@ -133,44 +138,6 @@ class JSTree extends AbstractBase
         return false;
     }
 
-    /**
-     * Convert JSTree JSON structure to HTML
-     *
-     * @param object $node     JSON object of a the JSTree
-     * @param string $recordID The currently active record
-     *
-     * @return string
-     */
-    public function jsonToHTML($node, $recordID = false)
-    {
-        $name = strlen($node->text) > 100
-            ? substr($node->text, 0, 100) . '...'
-            : $node->text;
-        $icon = $node->type == 'record' ? 'file-o' : 'folder-open';
-        $html = '<li';
-        if ($node->type == 'collection') {
-            $html .= ' class="hierarchy';
-            if ($recordID && $recordID == $node->li_attr->recordid) {
-                $html .= ' currentHierarchy';
-            }
-            $html .= '"';
-        } elseif ($recordID && $recordID == $node->li_attr->recordid) {
-            $html .= ' class="currentRecord"';
-        }
-        $html .= '><i class="fa fa-li fa-' . $icon . '"></i> '
-            . '<a name="tree-' . $node->id . '" href="' . $node->a_attr->href
-            . '" title="' . $node->text . '">'
-            . $name . '</a>';
-        if (isset($node->children)) {
-            $html .= '<ul class="fa-ul">';
-            foreach ($node->children as $child) {
-                $html .= $this->jsonToHTML($child, $recordID);
-            }
-            $html .= '</ul>';
-        }
-        return $html . '</li>';
-    }
-
     /**
      * Render the Hierarchy Tree
      *
@@ -186,7 +153,7 @@ class JSTree extends AbstractBase
             return false;
         }
         return json_encode(
-            $this->formatJSON(json_decode($json), $context, $hierarchyID)
+            $this->buildNodeArray(json_decode($json), $context, $hierarchyID)
         );
     }
 
@@ -197,13 +164,14 @@ class JSTree extends AbstractBase
      * @param string $context     Record or Collection
      * @param string $hierarchyID Collection ID
      *
-     * @return array/object
+     * @return array
      */
-    protected function formatJSON($node, $context, $hierarchyID)
+    protected function buildNodeArray($node, $context, $hierarchyID)
     {
+        $escaper = new \Zend\Escaper\Escaper('utf-8');
         $ret = [
             'id' => preg_replace('/\W/', '-', $node->id),
-            'text' => $node->title,
+            'text' => $escaper->escapeHtml($node->title),
             'li_attr' => [
                 'recordid' => $node->id
             ],
@@ -217,7 +185,7 @@ class JSTree extends AbstractBase
             $ret['children'] = [];
             for ($i = 0;$i<count($node->children);$i++) {
                 $ret['children'][$i] = $this
-                    ->formatJSON($node->children[$i], $context, $hierarchyID);
+                    ->buildNodeArray($node->children[$i], $context, $hierarchyID);
             }
         }
         return $ret;
@@ -255,6 +223,53 @@ class JSTree extends AbstractBase
         }
     }
 
+    /**
+     * Convert JSTree JSON structure to HTML
+     *
+     * @param object $node        JSON object of a the JSTree
+     * @param string $context     Record or Collection
+     * @param string $hierarchyID Collection ID
+     * @param string $recordID    The currently active record
+     *
+     * @return string
+     */
+    protected function jsonToHTML($node, $context, $hierarchyID, $recordID = false)
+    {
+        $escaper = new \Zend\Escaper\Escaper('utf-8');
+
+        $name = strlen($node->title) > 100
+            ? substr($node->title, 0, 100) . '...'
+            : $node->title;
+        $href = $this->getContextualUrl($node, $context, $hierarchyID);
+        $icon = $node->type == 'record' ? 'file-o' : 'folder-open';
+
+        $html = '<li';
+        if ($node->type == 'collection') {
+            $html .= ' class="hierarchy';
+            if ($recordID && $recordID == $node->id) {
+                $html .= ' currentHierarchy';
+            }
+            $html .= '"';
+        } elseif ($recordID && $recordID == $node->id) {
+            $html .= ' class="currentRecord"';
+        }
+        $html .= '><i class="fa fa-li fa-' . $icon . '"></i> '
+            . '<a name="tree-' . $escaper->escapeHtmlAttr($node->id) . '" href="'
+            . $escaper->escapeHtmlAttr($href) . '" title="'
+            . $escaper->escapeHtml($node->title) . '">'
+            . $escaper->escapeHtml($name) . '</a>';
+        if (isset($node->children)) {
+            $html .= '<ul class="fa-ul">';
+            foreach ($node->children as $child) {
+                $html .= $this->jsonToHTML(
+                    $child, $context, $hierarchyID, $recordID
+                );
+            }
+            $html .= '</ul>';
+        }
+        return $html . '</li>';
+    }
+
     /**
      * Transforms Collection XML to Desired Format
      *