Since your JSON object has variable keys, you must transform it into a fixed, predictable structure first or nested template mapping will not work (knockout is declarative, so you need to know key names beforehand).
Consider the following custom mapping code (no knockout mapping plugin needed):
var ListModel = function(jsonData) {
var self = this;
self.master = ko.observableArray([]);
function nestedMapping(data, level) {
var key, value, type;
for (key in data) {
if (data.hasOwnProperty(key)) {
if (data[key] instanceof Object) {
type = "array";
value = ko.observableArray([]);
nestedMapping(data[key], value());
} else {
type = "simple";
value = ko.observable(data[key]);
}
level.push({key: key, type: type, value: value});
}
}
}
nestedMapping(jsonData, self.master());
}
the function nestedMapping() turns your data structure:
{
"Level 1a": "Hi",
"Level 1b": {
"Level 2a": "Hello",
"Level 2b": {
"Level 3": "Bye"
}
}
}
into:
[
{
"key": "Level 1a",
"type": "simple",
"value": "Hi"
},
{
"key": "Level 1b",
"type": "array",
"value": [
{
"key": "Level 2a",
"type": "simple",
"value": "Hello"
},
{
"key": "Level 2b",
"type": "array",
"value": [
{
"key": "Level 3",
"type": "simple",
"value": "Bye"
}
]
}
]
}
]
Now you can create a template like this one:
<script type="text/html" id="nestedTemplate">
<!-- ko if: type == 'simple' -->
<div class="name" data-bind="text: value, attr: {title: key}"></div>
<!-- /ko -->
<!-- ko if: type == 'array' -->
<div class="container" data-bind="
template: {
name: 'nestedTemplate',
foreach: value
}
"></div>
<!-- /ko -->
</script>
See it working: http://jsfiddle.net/nwdhJ/2/
Note a subtle but important point about nestedMapping(). It creates nested observables/observableArrays. But it works with the native array instances (by passing self.master() and value() into the recursion).
This way you avoid needless delay during object construction. Every time you push values to an observableArray it triggers knockout change tracking, but we don't need that. Working with the native array will be considerably faster.