Necesitaba documentar Open.NAT y colgarlo en la wiki del proyecto en GitHub pero no encontré nada, así que escribí un transformador XML—> Markdown que me sirviera. La verdad es que el código, si bien son solo 100 LoC, está bastante sucio pero si a alguien le sirve aquí se los dejo.
Y este es el resultado:
Con esto lo invoco:
- class Program
- {
- static void Main(string[] args)
- {
- var xml = File.ReadAllText(args[0]);
- var doc = XDocument.Parse(xml);
- var md = doc.Root.ToMarkDown();
- Console.WriteLine(md);
- }
- }
Aquí está el código:
- static class XmlToMarkdown
- {
- static IEnumerable<string> DocToMarkDown(XNode e)
- {
- var el = (XElement) e;
- var members = el.Element(«members»).Elements(«member»);
- return new[]
- {
- el.Element(«assembly»).Element(«name»).Value,
- string.Join(«», members.Where(x => x.Attribute(«name»).Value.StartsWith(«F:»)).ToMarkDown()),
- string.Join(«», members.Where(x => x.Attribute(«name»).Value.StartsWith(«M:»)).ToMarkDown()),
- string.Join(«», members.Where(x => x.Attribute(«name»).Value.StartsWith(«E:»)).ToMarkDown())
- };
- }
- internal static string ToMarkDown(this XNode e)
- {
- var templates = new Dictionary<string, string>
- {
- {«doc», «## {0} ##nn## Fieldsnn{1}nn## Methodsnn{2}nn## Eventsnn{3}nn»},
- {«field», «### {0}nn{1}nn»},
- {«method», «### {0}nn{1}nn»},
- {«event», «### {0}nn{1}nn»},
- {«summary», «{0}nn»},
- {«remarks», «**remarks**nn{0}nn»},
- {«example», «**example**nn{0}nn»},
- {«see», «[{1}]({0})»},
- {«param», «_{0}_: {1}» },
- {«exception», «_{0}_: {1}nn» },
- {«returns», «Returns: {0}nn»},
- {«none», «»}
- };
- var d = new Func<string, XElement, string[]>((att, node) => new[]
- {
- node.Attribute(att).Value,
- node.Nodes().ToMarkDown()
- });
- var methods = new Dictionary<string, Func<XElement, IEnumerable<string>>>
- {
- {«doc», DocToMarkDown},
- {«field», x=> d(«name», x)},
- {«method»,x=>d(«name», x)},
- {«event», x=>d(«name», x)},
- {«summary», x=> new[]{ x.Nodes().ToMarkDown() }},
- {«remarks», x => new[]{x.Nodes().ToMarkDown()}},
- {«example», x => new[]{x.Value.ToCodeBlock()}},
- {«see», x=>d(«cref»,x)},
- {«param», x => d(«name», x) },
- {«exception», x => d(«cref», x) },
- {«returns», x => new[]{x.Nodes().ToMarkDown()}},
- {«none», x => new string[0]}
- };
- string name;
- if(e.NodeType== XmlNodeType.Element)
- {
- var el = (XElement) e;
- name = el.Name.LocalName;
- if (name == «member»)
- {
- switch (el.Attribute(«name»).Value[0])
- {
- case ‘F’: name = «field»; break;
- case ‘E’: name = «event»; break;
- case ‘M’: name = «method»; break;
- default: name = «none»; break;
- }
- }
- var vals = methods[name](el).ToArray();
- string str=«»;
- switch (vals.Length)
- {
- case 1: str= string.Format(templates[name], vals[0]);break;
- case 2: str= string.Format(templates[name], vals[0],vals[1]);break;
- case 3: str= string.Format(templates[name], vals[0],vals[1],vals[2]);break;
- case 4: str= string.Format(templates[name], vals[0], vals[1], vals[2], vals[3]);break;
- }
- return str;
- }
- if(e.NodeType==XmlNodeType.Text)
- return Regex.Replace( ((XText)e).Value.Replace(‘n’, ‘ ‘), @»s+», » «);
- return «»;
- }
- internal static string ToMarkDown(this IEnumerable<XNode> es)
- {
- return es.Aggregate(«», (current, x) => current + x.ToMarkDown());
- }
- static string ToCodeBlock(this string s)
- {
- var lines = s.Split(new char[] {‘n’}, StringSplitOptions.RemoveEmptyEntries);
- var blank = lines[0].TakeWhile(x => x == ‘ ‘).Count() – 4;
- return string.Join(«n»,lines.Select(x => new string(x.SkipWhile((y, i) => i < blank).ToArray())));
- }
- }