Search code examples
c#xmlparsings-expression

How to parse an XML to S-Expression for lisp using C#?


I want to be able to load any xml file and convert it into s-expression for lisp. Does anyone have an idea how this could be done using c#?

I have no prior experience in working either with lisp or s-expression and my main problem is that I can't figure out how to represent s-expression structure in code.

I'd appreciate any hint on that.

I was thinking of using linq to xml and maybe deserialize this xml to c# objects, and then serialize it to s-expression and but I'm not sure if that's the right way to do this.

e.g. That's what I load:

<document author="paul@prescod.net">
<para>This is a paragraph <footnote>(just a little one)</footnote>.</para>
<para>Ha! I made you say "underwear".</para>
</document>

That's what I would like to receive:

(document author: "paul@prescod.net"
  (para "This is a paragraph " (footnote "(better than the one under there)") ".")
  (para "Ha! I made you say \"underwear\"."))

Solution

  • You may use visitor pattern over XmlReader to traverse the xml and generate S-Expression alongside. This example generates quite close output (replace Console with a StringBuilder object if output is required as a string):

       var t = @"<document author=""paul@prescod.net"">
    <para>This is a paragraph <footnote>(better than the one under there)</footnote>.</para>
    <para>Ha! I made you say ""underwear"".</para>
    </document>";
    
        var xmlTextReader = XmlTextReader.Create(new StringReader(t));
    
        Visit(xmlTextReader, (XmlNodeType nodeType, XmlReader element) =>
        {
            switch (nodeType)
            {
                case XmlNodeType.Element:
                    Console.WriteLine();
                    Console.Write(new string('\t', element.Depth));
                    Console.Write("(" + element.Name);
                    break;
                case XmlNodeType.Text:
                    if(!string.IsNullOrEmpty(element.Value))
                    {
                        Console.Write(@" """ + element.Value.Replace(@"""", @"\""") + @"""");
                    }
                    break;
                case XmlNodeType.EndElement:
                    Console.Write(")");
                    break;
                case XmlNodeType.Attribute:
                    Console.Write(" " + element.Name + @": """ + element.Value.Replace(@"""",@"\""") + @"""");
                    break;
            }
        });
    ...
    
    
    public static void Visit(XmlReader xmlReader, Action<XmlNodeType, XmlReader> visitor)
    {
        while (xmlReader.Read())
        {
            visitor(xmlReader.NodeType, xmlReader);
    
            if (xmlReader.NodeType == XmlNodeType.Element)
            {
                while (xmlReader.MoveToNextAttribute())
                {
                    visitor(xmlReader.NodeType, xmlReader);
                }
            }
        }
    }