public string Write() { var doc = new XDocument(); var record = new XElement("Defs"); doc.Add(record); var writerContext = new WriterContext(false); foreach (var defObj in Database.List) { var defXml = Serialization.ComposeElement(defObj, defObj.GetType(), defObj.GetType().ComposeDefFormatted(), writerContext, isRootDef: true); defXml.Add(new XAttribute("defName", defObj.DefName)); record.Add(defXml); } writerContext.DequeuePendingWrites(); return(doc.ToString()); }
/// <summary> /// Returns a fully-formed XML document starting at an object. /// </summary> public static string Write <T>(T target, bool pretty = true) { var doc = new XDocument(); var record = new XElement("Record"); doc.Add(record); record.Add(new XElement("recordFormatVersion", 1)); var refs = new XElement("refs"); record.Add(refs); var writerContext = new WriterContext(true); var rootElement = Serialization.ComposeElement(target, target != null ? target.GetType() : typeof(T), "data", writerContext); record.Add(rootElement); // Handle all our pending writes writerContext.DequeuePendingWrites(); // We now have a giant XML tree, potentially many thousands of nodes deep, where some nodes are references and some *should* be in the reference bank but aren't. // We need to do two things: // * Make all of our tagged references into actual references in the Refs section // * Tag anything deeper than a certain depth as a reference, then move it into the Refs section var depthTestsPending = new List <XElement>(); depthTestsPending.Add(rootElement); // This is a loop between "write references" and "tag everything below a certain depth as needing to be turned into a reference". // We do this in a loop so we don't have to worry about ironically blowing our stack while making a change required to not blow our stack. while (true) { // Canonical ordering to provide some stability and ease-of-reading. foreach (var reference in writerContext.StripAndOutputReferences().OrderBy(kvp => kvp.Key)) { refs.Add(reference.Value); depthTestsPending.Add(reference.Value); } bool found = false; for (int i = 0; i < depthTestsPending.Count; ++i) { // Magic number should probably be configurable at some point found |= writerContext.ProcessDepthLimitedReferences(depthTestsPending[i], 20); } depthTestsPending.Clear(); if (!found) { // No new depth-clobbering references found, just move on break; } } if (refs.IsEmpty) { // strip out the refs 'cause it looks better that way :V refs.Remove(); } return(doc.ToString()); }