LinqToJSON – CodeProject article and source code released

I have released a small C# library called LinqToJSON.

What is LinqToJSON?

LinqToJSON is a .NET assembly, written in C#, that allows reading and writing data formatted according to the JSON standard with features that mimic those of LinqToXML. It is available for free, with its source code.
The assembly name is Ranslant.Linq.Json.

Documentation and source code

The full documentation has been posted as a CodeProject article, and you can find the source code there as well.
Read it all and do not hesitate to rate my work.

What does Ranslant.Linq.Json bring that other JSON writers/readers do not have?

  1. It does not use a serializer at all
    1. this ensures that the files written and read conforms very strictly to the standard
    2. This ensures that the files written can be read anywhere else (you often need to serialize/deserialize with the same serializer, because not all share the same conventions)
  2. It provides a set of classes that work in a way very similar to LinqToXML
    1. If you already worked with LinqToXML you can work with LinqToJSON
    2. It is very easy to read JSON data thanks to the querying power of LINQ
    3. It is very easy to write JSON data (check the article on CodeProject for examples)
  3. The data you give or you get is validated for correctness
  4. No errors codes, only thrown Exceptions
  5. The source code is provided, and made easy to read and understand
  6. The assembly is very small (14 KB)

Excerpts from the article follow.

Specifications (what I wanted)

Public objects

Similar to XDocument, XElement and XAttribute from LinqToXml, I want to have:

  • JDocument, as the root object. Derived class of JObject, to allow directly using the properties and methods of JObject.
  • Generic containers
    • JObject and JArray hold values. They implement the interface IJValue and provide accessors to the JSON content.
  • Specific containers
    • JString – I do not want to use the built-in type System.String directly, because conceptually a JSON string and a C# string are two different items. JString implements the interface IJValue and checks the validity of the given C# string, according to the JSON specifications.
    • JNumber – Same story here as to why I do not use the type Double directly. This class implements the interface IJValue and checks the validity of the given C# string, regarding the JSON specifications for numbers.
    • JTrue, JFalse and JNull – They also implement the interface IJValue. Again, I did not want to use the types bool and object directly.

Reading JSON

  1. Provide a static method JDocument.Load()
  2. Accessors will return
    1. IEnumerable<IJvalue> when the calling object is a JObject or Jarray, to allow for Linq-style querying
    2. a value of the appropriate type and content for the containers: ‘bool’ for JTrue and JFalse, ‘double’ for JNumber, ‘string’ for JString and ‘object’ for JNull.
  3. The generic type is IJValue. Filtering the values returned can be done over the type, using the keyword is.

code snippet

// first, load a file
JDocument jDoc = JDocument.Load(filename);

// then, you can use LINQ to parse the document
var members = from member in jDoc.GetMembers() select member;
foreach (var member in members)
{
	// you can filter the values using their type
	if (member.Value is JString || member.Value is JNumber)
		// do something ...

	// you can filter the values using their type
	if (member.Value is JObject)
	{
		JObject obj = member.Value as JObject;

		var names = from name in obj.GetNames() select name;
		foreach (var name in names)
		{
			// you can get an object with its name
			IJValue value = obj[name];

			if (value is JFalse || value is JTrue || value is JNull)
				// do something ...
		}

		var members = from member in obj.GetMembers() select member;
		foreach (var member in members)
		{
			Console.WriteLine("\t{0} : {1}", member.Name, member.Value.ToString());
		}
	}
}

Writing JSON

  1. Create a new JDocument instance
  2. Add content in several ways.
    1. either in the constructors of the JSON values directly
    2. or with the family of methods Add()
  3. Each class implementing the interface IJValue provides a method returning a string that contains the correct representation. For most object I do not use ToString() because I want to keep the JSON string representation of this object independent of C#.
  4. Provide a JDocument instance method Save()

See the classes’ description below and the code of the demo application for more details.

code snippet #1

// example of JDocument written only through the constructor:
new JDocument(
	new JObjectMember("D'oh",
		new JObject(
			new JObjectMember("First Name", new JString("Homer")),
			new JObjectMember("Family Name", new JString("Simpson")),
			new JObjectMember("Is yellow?", new JTrue()),
			new JObjectMember("Children",
				new JArray(
					new JString("Bart"),
					new JString("Lisa"),
					new JString("Maggie"))
			)
		)
	),
	new JObjectMember("never gets older",
		new JObject(
			new JObjectMember("First Name", new JString("Bart")),
			new JObjectMember("Family Name", new JString("Simpson"))
		)
	)
).Save("simpsons.json");

There are several interesting aspects in the example above:

  • except the final call to Save() all the data is created through constructors
  • the code can be presented in a very visual way, that mimics the structure of the JSON data
  • This JSON data can most likely not be deserialized, mostly because of the non-alphanumerical characters in the names of the object members

code snippet #2

// you can create an empty object and add values
JObject obj = new JObject();
obj.Add("_number", new JNumber(-3.14));
obj.Add("_true", new JTrue()); // notice the use of JTrue
obj.Add("_null", new JNull()); // notice the use of JNull

// you can create an empty array and add values
JArray arr = new JArray();
// ... either only one value
arr.Add(new JNumber("-15.64"));
// ... or more than one at once
// Notice that prefixing your strings with @ will help keeping them as valid JSON strings
arr.Add(new JString(@"Unicode: \u12A0"),
	new JString(@"\n\\test\""me:{}"));

JDocument doc = new JDocument(
	new JObjectMember("_false", new JFalse()),
	new JObjectMember("_false2", new JFalse())
); // the same name cannot be used twice!

// Add() has two forms:
// 1. with JObjectMember, you can give one or more
doc.Add(
	new JObjectMember("_array", arr),
	new JObjectMember("_string1", new JString("string1")),
);
// 2. directly give the name and the value
doc.Add("_obj", obj);

doc.Save(filename);

Points of Interest

Source code

I have tried to write code that is easy to understand yet efficient enough. I have avoided complex constructs and design patterns, because reading/writing JSON data is, in itself, not a complex task and therefore should not require complex code.
A few things are worth mentioning for the beginners in C#:

  • Use of regular expressions (in JNumber for instance)
  • Lazy creation of the data structures in JObject and JArray (Add()).The private data containers are initialized only once they are actually needed.
  • Use of an extension method (ConsumeToken() )
  • Use of an interface as generic base type (IJValue) , in combination with a simple factory pattern (JParser.ParseValue())
  • Methods with variable number of arguments (e.g. Add(params JObjectMember[] jObjectMembers))
  • Use of IEnumerable<T> to benefit from Linq’s querying power

About the parser

There are hundreds of ways to write a text parser. It is my opinion that all of them are right as long as the text is properly parsed and you get the specified (and hopefully expected) result. Which means: my parser is good because it works. This is all I expect from it.

What I do here is simple. I parse the text looking for tokens. Those tokens represent delimiters for specific JSON value types. In other words a token or a set of tokens give away the expected value type. The text between those tokens is then checked for validity, which means I check if what I have actually represents the value type that I expect from the delimiters.

What the parser does is follow the grammatical rules of JSON and report an error (by throwing an exception) when a rule is violated.

The way the string is technically parsed is by eating it up, so to speak, until it is empty. In other words, what is checked (whether tokens or content) is removed from the string to be parsed. Picture Pac-Man eating the pills in the maze, one pill at a time. Well, same thing here. 🙂

To achieve this I have chosen the following code structure:

  1. A class field containing the string that will be consumed. This field is not static since I want to stay thread safe. Each parsing step will modify this string, by removing the current token or content.
  2. A set of parsing function, each one devoted to recognizing and/or filling one and only one type of value. Which one is called depends on the token found.

Converting a base class to one of its derived classes

The only specific difficulty of the parser was getting ParseDocument(string) to return a JDocument instance although it was actually parsing a JObject.
First I had the following code:

JDocument jDoc = (JDocument)ParseObject();

It compiled, but threw an InvalidCastException (see tthis MSDN article and search for ‘Giraffe’). Indeed there is no equivalent to the C++ dynamic_cast in C#, and therefore you cannot convert a base class into one of its derived classes.

There are a few solutions for this issue:

  1. SELECTED SOLUTION – Copy the members of the object returned by ParseObject() into the base of the JDocument object (using the constructor JDocument(JObject jObject)). This works well and requires on-ly very little code. Besides, this solution allows to generally create a JDocument out of any JObject, which can also be useful.
  2. Encapsulate a JObject instance in JDocument. I found this to be not as nice, in terms of usability, as the first solution.
  3. use the CopyTo() method of JObject. This uses reflection (and can therefore bring per-formance issues) and is not guaranteed to work according to this post on MS Forums)
  4. derive JDocument from IJValue. But then I loose inheriting the JObject methods and I would have needed to duplicate them in JDocument. This is definitely not clean.

I found the solution #1 to be the only clean and elegant solution, but it works only because in JObject the fields and properties (there’s actually only one field) are accessed for writing only through public accessor functions (the Add() functions) that can be used freely in JDocument thanks to the inheritance.
This means that this pattern cannot be used generically.

Read the source code and the comments there for more information.

— End of excerpts

This was a fun little project, and even THE Sascha Barber wrote that my work is great! 😀
Say what you want, I’m proud.

Now I have to find a subject matter for the next article…

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s