Category Archives: .Net

Manually Parsing a Json String Using System.Text.Json

In this post from 2016 I gave some details as to how you could manually parse a JSON string using Newtonsoft JSON.NET. What I don’t think I covered in that post, was why you may wish to do that, nor did I cover how you could do that using System.Text.Json (although since the library was only introduced in .Net Core 3 – circa 2019, that part couldn’t really be helped!)

Why?

Let’s start with why you would want to parse a JSON string manually – I mean, the serialisation functions in, pretty much, any JSON library are really good.

The problem is coupling: by using serialisation, you’re coupling your data to a given shape, and very tightly coupling it to that shape, too. So much so, that a common pattern if you’re passing data between two services, and using serialisation, it to share the model class between the services. This sounds quite innocuous at first glance, but let’s consider a few factors (I’m assuming we’re talking exclusively about .Net, but I imagine the points are valid outside of that, too):

1. Why are you serialising and de-serialising the data to begin with?
2. Single Responsibility Principle.
3. Microservices.

Let’s start with (1). Your answer may be different, but typically, if you’re passing data as a string, it’s because you’re trying to remove a dependency to a given complex type. After all, a string can be passed anywhere: an API call, a message broker, even held in a database.

What has this got to do with the SRP (2)? Well, the SRP is typically used to describe the reason that a module has to change (admittedly it is slightly mis-named). Let’s see how the two modules may interact:

Now, let’s look at the interaction with a common module:

As you can see, they both have a dependency on a single external (external to the service) dependency. If the CustomerModel changes, then both services may also need to change, but they also need to change for alterations for business rules that relate to the module itself: so they now have two reasons to change.

Of course, you don’t have to have a common dependency like this; you could structure your system like this:

However, you don’t solve your problem – in fact, you arguably make it worse: if you change CustomerModel referenced by Service 1 you potentially break Service 2, so you now need to change CustomerModel referenced by Service 2, and Service 2!

Just to clarify what I’m saying here: there may be valid reasons for both of these designs – but if you use them, then be aware that you’re coupling the services to each other; which brings us to point (3): if you’re trying to create a Service Oriented Architecture of any description, then this kind of coupling may hinder your efforts.

The Solution

A quick caveat here: whatever you do in a system, the parts of that system will be coupled to some extent. For example, if you have built a Microservice Architecture where your system is running a nuclear reactor, and then you decide to change one of the services from monitoring the cooling tower to, instead, mine bit-coins, you’re going to find that there is an element of coupling. Some of that will be business coupling (i.e. the cooling tower may overheat), but some will be technical – a downstream service may depend on the monitoring service to assert that something is safe.

Apologies, I know absolutely nothing about nuclear reactors; or, for that matter, about mining bit-coin!

All that said, if you manually parse the JSON that’s received, you remove some dependency on the shape of the data.

The following reads a JSON document, and iterates through an array:

            using var doc = JsonDocument.Parse(json);
            var element = doc.RootElement;

            foreach (var eachElement in element.EnumerateArray())
            {
                string name = eachElement.GetProperty("Name").GetString();
                decimal someFigure = eachElement.GetProperty("SomeFigure").GetDecimal();

                if (someFigure > 500)
                {
                    Console.WriteLine($"{name} has more than 500!");
                }
            }

As you can see, if the property name of SomeFigure changed, the code would break; however, there may be a dozen more fields in each element that could change, and we wouldn’t care.

Compiling Code Files in C# Using Roslyn

I started investigating this for a unit test. I wanted to compile a separate project, and then copy the output into the test directory. As it happens, by the time I’d figured this out in Roslyn, I’d worked out it probably wasn’t right for the test that I was writing; however, given the amount of effort involved in actually piecing together the tiny amount of documentation available, I definitely thought it was worthy of documenting.

This particular post will focus on reading code files and compiling them into an executable or library. I may play around with this some more in the future: I suspect this might have quite a lot of mileage in the unit test space.

Dependencies

Let’s start with the dependencies:

    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.11.0" />
    <PackageReference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" Version="3.6.0" />

It’s worth bearing in mind that there are a few other libraries knocking about from previous incarnations of Roslyn, and similar tools; however, it’s my understanding that these are not in the Microsoft namespace. I probably spent the first couple of hours trying to get this to work with CSharpCodeProvider. If you found your way here searching for this error:

System.PlatformNotSupportedException: ‘Operation is not supported on this platform.’

Then you’re in good company.

Code

The following code will produce a library called qwerty.dll; it requires a variable giving the sourceFilesLocation and another, destinationLocation:

	// 1
            DirectoryInfo d = new DirectoryInfo(sourceFilesLocation); 
            string[] sourceFiles = d.EnumerateFiles("*.cs", SearchOption.AllDirectories)                
                .Select(a => a.FullName).ToArray();
            
	// 2
            List<SyntaxTree> trees = new List<SyntaxTree>();
            foreach (string file in sourceFiles)
            {
                string code = File.ReadAllText(file);
                SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
                trees.Add(tree);
            }
            
	// 3
            MetadataReference mscorlib =
                    MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
            MetadataReference codeAnalysis =
                    MetadataReference.CreateFromFile(typeof(SyntaxTree).Assembly.Location);
            MetadataReference csharpCodeAnalysis =
                    MetadataReference.CreateFromFile(typeof(CSharpSyntaxTree).Assembly.Location);

            MetadataReference[] references = { mscorlib, codeAnalysis, csharpCodeAnalysis };

	// 4
            var compilation = CSharpCompilation.Create("qwerty.dll",
                   trees,
                   references,
                   new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
            var result = compilation.Emit(Path.Combine(destinationLocation, "qwerty.dll"));

Let’s break the code down (you may notice that there are 4 comments, that to the untrained eye, appear useless).

1. The first part scans the source files directory and returns a list of files.
2. We then build a syntax tree list by reading all the files and calling the ParseText method on each; they are added to a list of parsed files.
3. We now build up a list of references needed to compile the code.
4. Finally, we create the dll, passing in the files that we built up in 2 & 3. .Create creates an in-memory version of the dll, so we then need to call .Emit to write it to disk.

Summary and caveats

This is a very simplistic example – as I’ve said, I need to spend some more time looking at some of the more advanced cases, but if you have a very simple library, this will compile and produce the DLL for you.

References

https://stackoverflow.com/questions/54364785/c-sharp-compile-c-sharp-code-at-runtime-with-roslyn

https://blog.dangl.me/archive/integration-testing-in-memory-compiled-code-with-roslyn/

https://www.tugberkugurlu.com/archive/compiling-c-sharp-code-into-memory-and-executing-it-with-roslyn

https://docs.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/get-started/syntax-transformation

https://stackoverflow.com/questions/32769630/how-to-compile-a-c-sharp-file-with-roslyn-programmatically