private void ProcessChildNode(HashSet <XNode> invalidNodes, Delta newDelta, XNode node, ParseState state, JObject attributes = null) { switch (node) { case XElement elem: switch (elem.Name.LocalName) { case "para": ProcessChildNodes(invalidNodes, newDelta, elem); InsertPara(invalidNodes, newDelta, elem); break; case "verse": state.LastVerseStr = (string)elem.Attribute("number"); InsertVerse(invalidNodes, newDelta, elem, state); break; case "ref": var newRefAttributes = (JObject)attributes?.DeepClone() ?? new JObject(); newRefAttributes.Add(new JProperty(elem.Name.LocalName, GetAttributes(elem))); newRefAttributes = AddInvalidInlineAttribute(invalidNodes, elem, newRefAttributes); newDelta.InsertText(elem.Value, state.CurRef, newRefAttributes); break; case "char": var newChildAttributes = (JObject)attributes?.DeepClone() ?? new JObject(); JToken existingCharAttrs = newChildAttributes["char"]; JObject newCharAttrs = GetAttributes(elem); if (!newCharAttrs.ContainsKey("cid")) { newCharAttrs.Add("cid", GuidService.Generate()); } if (existingCharAttrs == null) { newChildAttributes.Add(new JProperty(elem.Name.LocalName, newCharAttrs)); } else { switch (existingCharAttrs) { case JArray array: array.Add(newCharAttrs); break; case JObject obj: newChildAttributes[elem.Name.LocalName] = new JArray(obj, newCharAttrs); break; } } newChildAttributes = AddInvalidInlineAttribute(invalidNodes, elem, newChildAttributes); if (!elem.Nodes().Any() && elem.Value == "") { newDelta.InsertEmpty(state.CurRef, newChildAttributes); } else { ProcessChildNodes(invalidNodes, newDelta, elem, state, newChildAttributes); } break; case "table": state.TableIndex++; JObject tableAttributes = GetAttributes(elem); tableAttributes.Add(new JProperty("id", $"table_{state.TableIndex}")); int rowIndex = 1; foreach (XElement row in elem.Elements("row")) { var rowAttributes = new JObject( new JProperty("id", $"row_{state.TableIndex}_{rowIndex}")); int cellIndex = 1; foreach (XElement cell in row.Elements()) { state.CurRef = $"cell_{state.TableIndex}_{rowIndex}_{cellIndex}"; ProcessChildNode(invalidNodes, newDelta, cell, state); SegmentEnded(newDelta, state.CurRef); var attrs = new JObject( new JProperty("table", tableAttributes), new JProperty("row", rowAttributes)); if (cell.Name.LocalName == "cell") { attrs.Add(new JProperty("cell", GetAttributes(cell))); } attrs = AddInvalidBlockAttribute(invalidNodes, elem, attrs); attrs = AddInvalidBlockAttribute(invalidNodes, row, attrs); attrs = AddInvalidBlockAttribute(invalidNodes, cell, attrs); newDelta.Insert("\n", attrs); cellIndex++; } rowIndex++; } state.CurRef = null; break; case "cell": ProcessChildNodes(invalidNodes, newDelta, elem, state); break; default: InsertEmbed(invalidNodes, newDelta, elem, state.CurRef, attributes); break; } break; case XText text: newDelta.InsertText(text.Value, state.CurRef, AddInvalidInlineAttribute(invalidNodes, text, attributes)); break; } }