ESV Bible Web Service Client for .NET 3.5

A while back, the guys over at the ESV Bible web site announced their new REST-based interface to replace their old SOAP interface.  This new interface provides the same functionality as the old, but allows for 5,000 queries per day instead of 500 previously and is based on REST architectural principles.  Because the service is fundamentally chatty, it made sense to switch to REST.  In the context of a Bible web service, it's hard to justify a 200-byte XML message when your actual request is 6 bytes ("John 1").  Also, because the method call is in the URI, the entire call is simplified all the more.

For those of you who are completely unfamiliar with REST interfaces, all you really need to know is that it's a resource (or noun) based architecture.  That is to say instead of calling, for example, a "GetItem" method, you simply access an "item" entity.  You access what the thing is, not what the thing does; kind of a web-based reversal of encapsulation.  In other words, instead of giving a server a command (a verb), you are accessing the resource directly (a noun).  There's obviously more to REST than this and you can get more information from this nice article titled "Building Web Services the REST Way".

RESTful architecture really is a nice way to telling a system what you want, not how to get it.  This is really the point of framework design and abstraction in general.  In light of this it's obvious to see that, as awesome as REST is, it's not how .NET developers want to think when working working on a project.  When I'm working with something I want to focus on the object at hand, not on the URLs and parameters.  For this reason, I built a .NET 3.5 framework that allows easy and efficient access to the new ESV Bible REST web service.  Here are some samples of how to use it:

Here's a simple passage query returning HTML data:

ESVBibleServiceV2 service = new ESVBibleServiceV2( );
String output = service.PassageQuery("Galatians 3:11");

With the flip of a switch you can turn it into plain text:

ESVBibleServiceV2 service = new ESVBibleServiceV2(OutputFormat.PlainText);
String output = service.PassageQuery("Galatians 3:11");

For more flexibility, you may use the provided parameter objects.  Using these in C# 3.0 is seamless thanks to object initializers:

PassageQueryParameters pqp = new PassageQueryParameters( ) { Passage = "John 14:6" };
ESVBibleServiceV2 service = new ESVBibleServiceV2(new PlainTextSettings( )
{
    LineLength = 100,
    Timeout = 30
});
String output = service.PassageQuery(pqp);

Here is a simple sample of accessing the verse of the day (in HTML without the audio link -- optional settings):

ESVBibleServiceV2 service = new ESVBibleServiceV2(new HtmlOutputSettings( )
{
    IncludeAudioLink = false
});
String output = service.DailyVerse( );

You can also access various reading plans via the provided .NET enumeration:

ESVBibleServiceV2 service = new ESVBibleServiceV2( );
String output = service.ReadingPlanQuery(new ReadingPlanQueryParameters( )
{
    ReadingPlan = ReadingPlan.EveryDayInTheWord
});

Searching is also streamlined:

ESVBibleServiceV2 service = new ESVBibleServiceV2( );
String output = service.Query("Justified");

Here is a length example showing how you can use the QueryInfoAsObject method to get information about a query as a strongly-type object:

ESVBibleServiceV2 service = new ESVBibleServiceV2( );
QueryInfoData result = service.QueryInfoAsObject("Samuel");

Console.WriteLine(result.QueryType);
Console.WriteLine("----------------------");
if (result.QueryType == QueryType.Passage) {
    Console.WriteLine("Passage: " + result.Readable);
    Console.WriteLine("Complete Chapter?: " + result.IsCompleteChapter);
    if (result.AlternateQueryType != QueryType.None) {
        Console.WriteLine(String.Format("Alternate: , ", result.AlternateQueryType, result.AlternateResultCount));
    }
}

if (result.HasWarnings) {
    foreach (Warning w in result.Warnings) {
        Console.WriteLine(String.Format(": ", w.Code, w.Readable));
    }
}

Here is the output:

QueryInfoAsObject Example Output

For more advanced users, the Crossway XML format is also available:

ESVBibleServiceV2 service = new ESVBibleServiceV2(new CrosswayXmlVersion10Settings( )
{
    IncludeWordIds = true,
    IncludeXmlDeclaration = true
});
String output = service.PassageQuery(new PassageQueryParameters( )
{
    Passage = "Galatians 3"
});
Console.WriteLine(output);

That same XML data is also retrievable as an XmlDocument for pure XML interaction:

ESVBibleServiceV2 service = new ESVBibleServiceV2( );
XmlDocument output = service.PassageQueryAsXmlDocument("Galatians 3");

For more flexible XML interaction, you may use XPath:

ESVBibleServiceV2 service = new ESVBibleServiceV2( );

String output = service.PassageQueryValueViaXPath(new PassageQueryParameters( )
{
    Passage = "Gal 3:4-5",
    XPath = "//crossway-bible/passage/surrounding-chapters/current"
});

Sometimes, however, you will want more than one result from XPath:

String[] output = service.PassageQueryValueViaXPathMulti(new PassageQueryParameters( )
{
    Passage = "Gal 3:4-5",
    XPathSet = new[]
    {
        "//crossway-bible/passage/surrounding-chapters/previous",
        "//crossway-bible/passage/surrounding-chapters/next"                
    }
});

Here's what the result looks like the debugger:

XPathSet Example Output

I've also copied the documentation for functions and parameters into the .NET XML comments, so you can quickly and easily see what a certain function or parameter does and it's default:

ESVBibleServiceXmlComment

The new API uses your existing ESV Bible Web Service access key.  To use this key in this framework you simply add an element called ESVBibleServiceKey to the addSettings in your configuration file (a sample is provided with the framework).  You may also set it in any one of the parameter objects (i.e. PassageQueryParameters, QueryParameters, etc...), which override the key in the configuration file.  Per the API, you can use TEST for testing and IP for for general purpose queries.

Lastly, I would like to mention that this framework minimizes traffic by only sending options that deviate from defaults. So, for example, if you set IncludeWordIds to false and IncludeXmlDeclaration to true, only the IncludeXmlDeclaration value will be sent over the wire since IncludeWordIds is false by default.

You can access this ESV Bible Web Service 2.0 framework on CodePlex at the address in the links section.  Enjoy!

Links