using System.Collections.Generic;
<sublist type='{{type}}' items='{{sublist}}'>
<subitem>{{name}}</subitem>
{""sublist"": [{""name"":""a""}, {""name"":""b""}], ""type"": ""type1""},
{""sublist"": [{""name"":""c""}, {""name"":""d""}], ""type"": ""type2""}
var jsonDoc = JsonDocument.Parse(jsonData);
var jsonRoot = jsonDoc.RootElement;
XDocument doc = XDocument.Parse(xml);
ProcessItems(doc.Root, jsonRoot);
var jsonFromXml = ConvertXmlToJsonOption2(doc.Root);
Console.WriteLine("\nConverted JSON:");
Console.WriteLine(JsonSerializer.Serialize(jsonFromXml, new JsonSerializerOptions { WriteIndented = true }));
static void ProcessItems(XElement parentElement, JsonElement jsonData, JsonElement? parentJsonData = null)
foreach (XElement element in parentElement.Elements().ToList())
XAttribute itemsAttr = element.Attribute("items");
if (itemsAttr != null && itemsAttr.Value.StartsWith("{{") && itemsAttr.Value.EndsWith("}}"))
string key = itemsAttr.Value.Trim('{', '}');
if (jsonData.TryGetProperty(key, out JsonElement listData) && listData.ValueKind == JsonValueKind.Array)
List<XElement> newElements = new List<XElement>();
foreach (JsonElement listItem in listData.EnumerateArray())
XElement newElement = new XElement(element);
ProcessAttributes(newElement, listItem, jsonData);
ProcessItems(newElement, listItem, jsonData);
newElements.Add(newElement);
element.ReplaceWith(newElements);
ProcessItems(element, jsonData, parentJsonData);
if (element.Name == "list" && itemsAttr != null)
ReplaceTextPlaceholders(element, jsonData, parentJsonData);
static void ProcessAttributes(XElement element, JsonElement jsonData, JsonElement? parentJsonData = null)
foreach (XAttribute attr in element.Attributes().ToList())
if (attr.Value.StartsWith("{{") && attr.Value.EndsWith("}}"))
string key = attr.Value.Trim('{', '}');
if (jsonData.TryGetProperty(key, out JsonElement value))
if (value.ValueKind == JsonValueKind.String)
attr.Value = value.GetString();
else if (parentJsonData.HasValue && parentJsonData.Value.TryGetProperty(key, out JsonElement parentValue))
if (parentValue.ValueKind == JsonValueKind.String)
attr.Value = parentValue.GetString();
static void ReplaceTextPlaceholders(XElement element, JsonElement jsonData, JsonElement? parentJsonData = null)
foreach (XNode node in element.Nodes().ToList())
if (node is XText textNode && textNode.Value.StartsWith("{{") && textNode.Value.EndsWith("}}"))
string key = textNode.Value.Trim('{', '}');
if (jsonData.TryGetProperty(key, out JsonElement value))
if (value.ValueKind == JsonValueKind.String)
textNode.ReplaceWith(new XText(value.GetString()));
else if (parentJsonData.HasValue && parentJsonData.Value.TryGetProperty(key, out JsonElement parentValue))
if (parentValue.ValueKind == JsonValueKind.String)
textNode.ReplaceWith(new XText(parentValue.GetString()));
static object ConvertXmlToJsonOption1(XElement element)
if (!element.HasElements)
if (element.HasAttributes)
var attributes = element.Attributes().ToDictionary(attr => attr.Name.LocalName, attr => (object)attr.Value);
if (!string.IsNullOrWhiteSpace(element.Value))
attributes["value"] = element.Value;
var groupedElements = element.Elements()
.GroupBy(child => child.Name.LocalName)
group => group.Count() > 1
? group.Select(ConvertXmlToJsonOption1).ToList()
: ConvertXmlToJsonOption1(group.First())
if (element.HasAttributes)
var attributes = element.Attributes().ToDictionary(attr => attr.Name.LocalName, attr => (object)attr.Value);
foreach (var attr in attributes)
groupedElements[attr.Key] = attr.Value;
static object ConvertXmlToJsonOption2(XElement element)
var jsonObject = new Dictionary<string, object>
{ "type", element.Name.LocalName }
if (element.HasAttributes)
jsonObject["params"] = element.Attributes().ToDictionary(attr => attr.Name.LocalName, attr => (object)attr.Value);
jsonObject["params"] = new Dictionary<string, object>();
jsonObject["children"] = element.Elements().Select(ConvertXmlToJsonOption2).ToList();
else if (!string.IsNullOrWhiteSpace(element.Value))
var paramsDict = (Dictionary<string, object>)jsonObject["params"];
paramsDict["value"] = element.Value;