An XmlSlurper implementation in .Net, both for Xml and Json. The idea came from Groovy's XmlSlurper which is hugely useful.
What this does, is convert a piece of xml, e.g.
<card xmlns="http://businesscard.org">
<name>John Doe</name>
<title>CEO, Widget Inc.</title>
<email>[email protected]</email>
<phone>(202) 456-1414</phone>
<logo url="widget.gif"/>
</card>
or json, e.g.
{
"card": {
"name": "John Doe",
"title": "CEO, Widget Inc.",
"email": "[email protected]",
"phone": "(202) 456-1414",
"logo": {
"url": "widget.gif",
}
}
}
to a C# object, e.g.
card.name
card.title
card.email
card.phone
card.logo.url
This is done without any need to declare the type. Behind the scenes it uses a class similar to System.Dynamic.ExpandoObject, named ToStringExpandoObject.
Under the Release tab you can find the binaries to download, but the recommended way is to use it as a nuget dependency. The nuget package is named Dandraka.Slurper, here: https://www.nuget.org/packages/Dandraka.Slurper .
The library contains two classes, XmlSlurper and JsonSlurper. Both of them are static and contain two methods: ParseFile(string path)
which accepts a filename and ParseText(string text)
which accepts xml and json text respectively.
Both of them have a settable string property ListSuffix
which has the default value of List. This is used when encountering arrays; a property is generated that is named as <commonName><ListSuffix>
. For example, parsing the following xml:
<?xml version="1.0" encoding="UTF-8"?>
<Groceries>
<name>Avocado Dip</name>
<mfr>Sunnydale</mfr>
<carb>2</carb>
<fiber>0</fiber>
<protein>1</protein>
</Groceries>
<Groceries>
<name>Bagels, New York Style</name>
<mfr>Thompson</mfr>
<carb>54</carb>
<fiber>3</fiber>
<protein>11</protein>
</Groceries>
<Groceries>
<name>Beef Frankfurter, Quarter Pound</name>
<mfr>Armitage</mfr>
<carb>8</carb>
<fiber>0</fiber>
<protein>13</protein>
</Groceries>
will return Bagels, New York Style under xmlObj.GroceriesList[1].name
.
In a similar way, parsing the following json:
{
'Groceries':
[
{
'name': 'Avocado Dip',
'mfr': 'Sunnydale',
'carb': '2',
'fiber': '0',
'protein': '1'
},
{
'name': 'Bagels, New York Style',
'mfr': 'Thompson',
'carb': '54',
'fiber': '3',
'protein': '11'
},
{
'name': 'Beef Frankfurter, Quarter Pound',
'mfr': 'Armitage',
'carb': '8',
'fiber': '0',
'protein': '13'
}
]
}
will return Bagels, New York Style under jsonObj.Groceries.GroceriesList[1].name
.
If the value of ListSuffix
is changed to, say, 'Catalogue', the above object with be jsonObj.Groceries.GroceriesCatalogue[1].name
.
using Dandraka.Slurper;
public void PrintXmlContents1()
{
string xml = "<book id=\"bk101\" isbn=\"123456789\"><author>Gambardella, Matthew</author><title>XML Developer Guide</title></book>";
var book = XmlSlurper.ParseText(xml);
// that's it, now we have everything
Console.WriteLine("id = " + book.id);
Console.WriteLine("isbn = " + book.isbn);
Console.WriteLine("author = " + book.author);
Console.WriteLine("title = " + book.title);
}
public void PrintXmlContents2()
{
string xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
"<nutrition>" +
" <food>" +
" <name>Avocado Dip</name>" +
" <mfr>Sunnydale</mfr>" +
" <carb>2</carb>" +
" <fiber>0</fiber>" +
" <protein>1</protein>" +
" </food>" +
" <food>" +
" <name>Bagels, New York Style </name>" +
" <mfr>Thompson</mfr>" +
" <carb>54</carb>" +
" <fiber>3</fiber>" +
" <protein>11</protein>" +
" </food>" +
" <food>" +
" <name>Beef Frankfurter, Quarter Pound </name>" +
" <mfr>Armitage</mfr>" +
" <carb>8</carb>" +
" <fiber>0</fiber>" +
" <protein>13</protein>" +
" </food>" +
"</nutrition>";
var nutrition = XmlSlurper.ParseText(xml);
// since many food nodes were found, a list was generated and named foodList (common name + "List")
Console.WriteLine("name1 = " + nutrition.foodList[0].name);
Console.WriteLine("name2 = " + nutrition.foodList[1].name);
}
public void ReadSettings()
{
var settings = XmlSlurper.ParseText(getFile("HardwareSettings.xml"));
if (!settings.view.displayIcons)
{
DoWhatever();
}
int? minFreeSpace = settings.performance.additionalChecks.disk.minFreeSpace;
// Implicit type conversion works for string, bool?, int?, double?, decimal?,
// bool, int, double and decimal.
// Note that if the xml content cannot be parsed (e.g. you try to use
// an xml node as bool but it contains "lalala") then for bool? you get null,
// and for bool you get a ValueConversionException.
}
using Dandraka.Slurper;
public void PrintJsonContents1_Simple()
{
string json =
@"{
'id': 'bk101',
'isbn': '123456789',
'author': 'Gambardella, Matthew',
'title': 'XML Developer Guide'
}".Replace("'", "\"");
var book = JsonSlurper.ParseText(json);
// that's it, now we have everything
Console.WriteLine("id = " + book.id);
Console.WriteLine("isbn = " + book.isbn);
Console.WriteLine("author = " + book.author);
Console.WriteLine("title = " + book.title);
}
public void PrintJsonContents2_Array()
{
string json =
@"{
'Groceries':
[
{
'name': 'Avocado Dip',
'mfr': 'Sunnydale',
'carb': '2',
'fiber': '0',
'protein': '1'
},
{
'name': 'Bagels, New York Style',
'mfr': 'Thompson',
'carb': '54',
'fiber': '3',
'protein': '11'
},
{
'name': 'Beef Frankfurter, Quarter Pound',
'mfr': 'Armitage',
'carb': '8',
'fiber': '0',
'protein': '13'
}
]
}".Replace("'", "\"");
JsonSlurper.ListSuffix = "Inventory";
var nutrition = JsonSlurper.ParseText(json);
// Since many nodes were found, a list was generated.
// It's named common name + "List", so in this case GroceriesList.
// But note that we've changed the value of ListSuffix to Inventory,
// so the list name will become GroceriesInventory.
Console.WriteLine("name1 = " + nutrition.Groceries.GroceriesInventory[0].name);
Console.WriteLine("name2 = " + nutrition.Groceries.GroceriesInventory[1].name);
}
public void PrintJsonContents3_TopLevelArray()
{
string json =
@"[
{
'name': 'Avocado Dip',
'mfr': 'Sunnydale',
'carb': '2',
'fiber': '0',
'protein': '1'
},
{
'name': 'Bagels, New York Style',
'mfr': 'Thompson',
'carb': '54',
'fiber': '3',
'protein': '11'
},
{
'name': 'Beef Frankfurter, Quarter Pound',
'mfr': 'Armitage',
'carb': '8',
'fiber': '0',
'protein': '13'
}
]".Replace("'", "\"");
var nutrition = JsonSlurper.ParseText(json);
// Since many nodes were found, a list was generated and named List.
// Normally it's named common name + "List" (e.g. GroceriesList)
// but in this case the parent of the array is nameless
// (it's the root) ergo just "List".
Console.WriteLine("name1 = " + nutrition.List[0].name);
Console.WriteLine("name2 = " + nutrition.List[1].name);
}
A VB.Net example:
Imports Dandraka.Slurper
Imports System
Imports System.Linq
Imports System.Collections.Generic
Imports System.IO
Public Module Module1
Public Sub Main()
Dim myXml As String = "<Vegetable><name>Avocado Dip</name><mfr>Sunnydale</mfr><nutrient><name>carb</name><value>2</value></nutrient><nutrient><name>fiber</name><value>1</value></nutrient><nutrient><name>protein</name><value>11</value></nutrient></Vegetable>"
Dim myVegetable
myVegetable = XmlSlurper.ParseText(myXml)
Console.WriteLine("Vegetable name: " & myVegetable.name.ToString)
Console.WriteLine("Vegetable manufacturer: " & myVegetable.mfr.ToString)
Console.WriteLine()
Dim myNutrientsList As List(Of Dandraka.Slurper.ToStringExpandoObject) = myVegetable.nutrientList
Dim proteinNutrient As Object = myNutrientsList.FirstOrDefault(Function(i) CType(i, Object).name = "protein")
Console.WriteLine(proteinNutrient.name.ToString() + " - " + proteinNutrient.value.ToString())
Console.ReadLine()
End Sub
End Module
Release 2.0 is renamed, from XmlUtilities to Slurper (since it's more than Xml now, duh 😊). It implements JsonSlurper alongside XmlSlurper and is fully backwards compatible with all previous versions; the only change needed is to change the using clauses, from:
using Dandraka.XmlUtilities;
to:
using Dandraka.Slurper;
Although not required by the license, the author kindly asks that you share any improvements you make.