Getting Java, C# and Perl to speak the same language (with JSON)

By Andrew Stellman
October 5, 2009 | Comments: 7

Using JSON examples in Java, C# and Perl to understand cross-platform architecture.

I've been thinking a lot about architecture lately. It's partially because Jenny and I are going to do our Beautiful Teams talk at the ITARC 2009 conference next week. But it's also because I've been writing a lot of code lately that's gotten me thinking about some architecture problems that I find pretty interesting. One of the problems I've been contending with has been how to efficiently get complex data structures to stay intact while being sent across various pipes and between different platforms. It's a really interesting topic, one that's really given me some new insights into some pretty fascinating questions about the nature of data, and where the data ends and the container begins.

Head First C# Cover

I spend a lot of my time training developers, helping junior and intermediate programmers learn the skills and concepts they need to become senior developers. So whenever I run across a topic like this, I always ask myself, "Is there something an intermediate -- or even a novice -- programmer can learn here?" I've spent a lot of my career helping junior programmers become senior developers and architects. (That's why we wrote Head First C#!) If you're a developer who's looking to expand your horizons a bit, this is definitely an area that can help you do that.

And the best way I know of to expand your horizons is to learn by example -- in this case, by building a very simple Java server that sends data using JSON, and then building a clients in Perl and C# that connect to the server and read the data. JSON (JavaScript Object Notation) is a language-independent text format for exchanging data. I like it as a way to teach about and experiment with this problem, because it's easy for humans to read JSON-encoded data.

The best way to learn about building software is to play around and experiment on your own. To do that, you need clear, simple, open-ended examples. So my goal with this post is to:

  • Help you start to get an idea of why getting different platforms to talk to each other may not be as straightforward as it seems
  • Use a Java example to show you what JSON looks like
  • Build a very simple Java server that transmits JSON objects
  • Then build a C# client that connects to it and reads the objects
  • Point to some of the next problems that this very simple "solution" would need to address
  • Use the simplest possible examples I can come up with to show how it all works.

One thing to keep in mind: the examples here are optimized to be easy to understand and learn from, not to give you a template for building systems. More about that at the end.

What you need to run this code

I'm going to assume that there's a good chance that you're either a Java programmer, a C# programmer, or a Perl programmer, but you're not necessarily all three. So since I'll be using examples from all three languages, I'll make sure to show you how to build and run all of the code from the command line. If you're not used to Java, make sure you've got JDK 6 installed. If you're not used to C#, make sure you've got .NET Framework 3.5 installed -- if you've updated Windows any time in the last few years, it should already be installed for you. The C# code will also compile and run using Mono 2.4. And you'll want Perl 5 installed to run the Perl code. (If you are an expert in any of those languages and just curious about the others or about how JSON works, please bear with me -- the extra explanation is there for people who are still new at this).

Speaking different languages

It's funny how we've gotten used to the idea that different software on different platforms can talk to each other seamlessly. I'm currently typing this on my Mac using a browser that's connected to a Linux server. Not only are they on different platforms, it's pretty unlikely that the browser and the server are written in the same language. But more importantly, we live in a world where this all works to the point where it's completely mundane. I can save a draft, pop over to my Parallels window running Windows XP, and resume it there, and I wouldn't be doing anything surprising.

It's not that long ago that this would have been amazing, eye-popping technology. Back in the early 1990s (when I was first getting programming jobs), if we had two different pieces of software on different operating systems written in different languages that use different communications protocols, we'd pretty much assume that getting them to talk to each other would be an expensive and error-prone undertaking. Yes, it was certainly possible, and there were communications frameworks that did it for you. But it wasn't an everyday thing, and it took work.

The reason is because you need a way to encode the data, and when you're dealing with different platforms -- like Java and .NET -- this isn't a straightforward problem to solve. We've all had to get data from one place to another. If you've ever had trouble because you couldn't get, say, your Mac spreadsheet to open up on Windows, you've run into exactly the problem that a developer working on a cross-platform architecture has to solve. The data in your spreadsheet is the same no matter what platform it's being displayed on. But the encoding -- in this case, the spreadsheet file format -- needs to change.

Getting Java to convert an object to JSON

There are plenty of explanations and examples of JSON around. But it's straightforward enough that the easiest way to get how it works is to see it in action. Luckily, there are also a lot of different libraries for many different platforms that write JSON -- there are dozens listed here. One of the easiest and most popular is google-gson. It's easy to use it to convert an object to JSON with just a couple of lines of code.

Here's a really simple Java class -- about as simple as it can get. It's just got a couple of fields, a toString() method, and a static factory method to build a random Thing.

Thing.java
import java.util.Random;

public class Thing {
     private String what;
 
     public String getWhat() {
          return what;
      }
 
     public int weight;
 
     public int getWeight() {
          return weight;
      }
 
     public Thing(String what, int weight) {
          this.what = what;
          this.weight = weight;
      }
 
     @Override
     public String toString() {
          return "A " + what + " that weighs "
                  + weight + " pounds\r\n";
      }
 
     private static Random random = new Random();
     private static String[] things = { "bowling ball",
              "car", "briefcase", "dog", "person", "rock", 
              "ladder", "rhino", "dictionary", "radiator" };
     private static int[] weights =
            { 16, 2600, 2, 46, 126, 75, 12, 1850, 3, 25 };
 
     public static Thing getRandomThing() {
          int i = random.nextInt(things.length);
          return new Thing(things[i], weights[i]);
      }
}

And here's a program to print a random Thing.

PrintRandomThing.java
public class PrintRandomThing {
     public static void main(String[] args) {
          System.out.println(Thing.getRandomThing().toString());
      }
}

If you're a .NET or Perl developer who's new to Java, here's how to build it. First, make sure that both java and javac executables are in your path. They're installed as part of JDK 6, and you may already have them installed. (Use java -showversion to check the version -- make sure it starts with 1.6.) Then save the two files, Thing.java and PrintRandomThing.java to a folder, build the code with java (which creates a .class file for each .java code file), and run it using javac:

C:\Code>javac Thing.java PrintRandomThing.java

C:\Code>java PrintRandomThing
A dictionary that weighs 3 pounds

C:\Code>java PrintRandomThing
A rhino that weighs 1850 pounds

(This works on Windows, Mac, Linux -- anywhere you can install Java.)

Now we're set up to get a first look at JSON using Google gson, which has a method toJson() that serializes an objects to a JSON string. So here's a new program, PrintJSONThing:

PrintJSONThing.java
import com.google.gson.Gson;

public class PrintJSONThing {
     public static void main(String[] args) {
          Thing thing = Thing.getRandomThing();
          System.out.println(thing.toString());
          Gson gson = new Gson();
          String jsonThing = gson.toJson(thing);
          System.out.println(jsonThing);
      }
}

To build this, you'll need the Google gson API, which you can download from the Google gson page. (I downloaded google-gson-1.3-release.zip from that page.) Extract the file gson-1.3.jar into the folder where you've got your Java source. (For .NET developers: a JAR is the Java equivalent of a .NET assembly -- a container that holds byte code compiled from source, which you can use as a reference in your programs.)

Once you've got Thing.java, PrintJSONThing.java, and gson-1.3.jar (or a later version) in a folder, here's how to build and run PrintJSONThing on Windows:

C:\Code>javac -cp gson-1.3.jar Thing.java PrintJSONThing.java

C:\Code>java -cp ".;gson-1.3.jar" PrintJSONThing
A briefcase that weighs 2 pounds

{"what":"briefcase","weight":2}

If you're using Mac, Linux or any other non-Windows OS, you'll need to replace the semicolon with a colon in the java command line's -cp argument:

bash$ javac -cp gson-1.3.jar Thing.java PrintJSONThing.java

bash$ java -cp ".:gson-1.3.jar" PrintJSONThing
A person that weighs 126 pounds

{"what":"person","weight":126}

Take a look at the last line that your program prints, because that's the JSON-encoded Thing:

{"what":"rhino","weight":1850}

You can see why it's easier to show how JSON works first, and then explain it once you've got an example in front of you. The JSON representation of a Thing object consists of the value of the "what" field ("briefcase" or "person" or "rhino") and the value of the "weight" field (2 or 126 or 1850). Each pair of a name and value is delimited with a colon, and the pairs are separated with commas and contained in curly braces.

So if you're a junior or intermediate developer looking to get some insight into one of the challenges of encoding data for transport between systems, here's something to think about: Why is it sufficient to just save the value of the fields? The JSON representation of the Thing only has the values of its "what" and "weight" fields, but it doesn't say anything about its methods. Take a minute and think about that. (Here's a hint: an Excel file and a SQL table might both contain the same data, but they're stored very differently. Where does the data end and the container begin?)

JSON is especially good at representing objects that contain other objects. Here's a Guy class that has a name and a list of Thing objects:

Guy.java

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Guy {
     private String name;
     public String getName() {
          return name;
      }
 	
     private List<Thing> stuff;
     public List<Thing> getStuff() {
          return stuff;
      }
 	
     public Guy(String name, List<Thing> stuff) {
          this.name = name;
          this.stuff = stuff;
      }
 	
     @Override
     public String toString() {
          String s = "My name is " + name 
              + " and I have the following stuff:\r\n";
          for (Thing thing : stuff) {
               s += thing;
           }
      return s;
 }
 
     private static Random random = new Random();
     private static String[] names = { "Bob", "Joe", "Bill", 
        "Ed", "Harry", "Biff", "Jimmy", "Dick", "Don", "Jack",
        "Paul", "Mike", "Eddie", "John", "Tom", "Lou",
        "Jeff", "Larry" };
     public static Guy getRandomGuy() {
          int i = random.nextInt(names.length);
          int j = random.nextInt(4);
          List<Thing> stuff = new ArrayList<Thing>();
          for (int k = 0; k <= j; k++) {
               stuff.add(Thing.getRandomThing());
           }
          Guy guy = new Guy(names[i], stuff);
          return guy;
      }
}

And here's a program that converts a Guy to JSON:

PrintJSONGuy.java
import com.google.gson.Gson;

public class PrintJSONGuy {
     public static void main(String[] args) {
          Guy guy = Guy.getRandomGuy();
          System.out.println(guy.toString());
          Gson gson = new Gson();
          String jsonGuy = gson.toJson(guy);
          System.out.println(jsonGuy);
      }
}

Build and run the program, and you'll get the following output (with a randomly generated Guy) -- remember to replace the colon with a semicolon if you're using Windows:

(Again, on a non-Windows OS, use -cp ".:gson-1.3.jar" instead, replacing the semicolon with a colon.)

C:\Code>javac -cp gson-1.3.jar Guy.java Thing.java PrintJSONGuy.java

C:\Code>java -cp ".;gson-1.3.jar" PrintJSONGuy
My name is Mike and I have the following stuff:
A person that weighs 126 pounds
A bowling ball that weighs 16 pounds
A ladder that weighs 12 pounds
A dog that weighs 46 pounds

{"name":"Mike","stuff":[{"what":"person","weight":126},{"what":"bowling ball","weight":16},{"what":"ladder","weight":12},{"what":"dog","weight":46}]}

Take a close look at how the JSON string represents the list of four Thing objects contained in the Guy. It uses the same "name":value format for the stuff List. But for the value, it uses a comma-separated list of JSON strings that represent Thing objects, contained in a pair of square brackets.

Serving a JSON-encoded object

Now that we've got a reliable way of generating JSON objects, the next step is to get it into a .NET program. And one of the easiest ways to do that, believe it or not, is to build a little TCP server that serves up random JSON-encoded Guy objects.

Warning: This is not a really effective way to build a server. It's optimized to use as few lines of code as possible in order to demonstrate a technology, rather than show you the "right" way to serve data over a network. For example, only one client can connect at once, which is something that server developers would refer to as "a really dumb way to design a server." This pattern does work really well as a learning and exploration tool, although you wouldn't want to build a web server this way.

GuyServer.java
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import com.google.gson.Gson;

public class GuyServer {
     public static void main(String[] args)
     throws IOException {
          Gson gson = new Gson();
          ServerSocket serverSocket = new ServerSocket(16001);
          System.out.println("Listening on port 16001. " 
                  + "Press enter to quit "
                  + "after the next connection.");
          while (System.in.available() == 0) {
               Socket socket = serverSocket.accept();
               System.out.println("A client has connected." 
                   + " Sending a new Guy down the pipe.");
               PrintWriter out =
                   new PrintWriter(socket.getOutputStream(),
                       true);
               Guy guy = Guy.getRandomGuy();
               String json = gson.toJson(guy);
               out.print(json);
               out.close();
               socket.close();
           }
          System.out.println("Quitting...");
          serverSocket.close();
      }
}

Now build and run the program. (Again, on a non-Windows OS, use -cp ".:gson-1.3.jar" instead, replacing the semicolon with a colon.)

C:\Code>javac -cp gson-1.3.jar Guy.java Thing.java GuyServer.java

C:\Code>java -cp ".;gson-1.3.jar" GuyServer
Listening on port 16001. Press enter to quit after the next connection.

Your little server is listening on port 16001. (If you're running Windows, your Windows Firewall may pop up a warning message asking you if you want to unblock your server. Click "Unblock".) Now, from another window telnet to your machine to see it serve up a JSON-encoded Guy object:

C:\>telnet localhost 16001
{"name":"Jack","stuff":[{"what":"dog","weight":46},{"what":"rhino","weight":1850}]}

Connection to host lost.

C:\>telnet localhost 16001
{"name":"Ed","stuff":[{"what":"rock","weight":75},{"what":"dog","weight":46},{"what":"radiator","weight":25}]}

Connection to host lost.

Go back to the server window -- you should see that your server printed two lines, one for each connection. Press enter to tell your server to quit after the next incoming connection. Then connect one more time with telnet, and watch your server quit.

Listening on port 16001. Press enter to quit after the next connection.
A client has connected. Sending a new Guy down the pipe.
A client has connected. Sending a new Guy down the pipe.

A client has connected. Sending a new Guy down the pipe.
Quitting...

Your server is now sending JSON data to any client that happens to connect to it. So now we just need to build a client. But let's throw a wrench in the gears. Instead of building a client in Java...

Build a client in C# that connects to the server

Now that you've got your server that serves up JSON objects, it's time to put the next piece in place: a client built in C# that reads that object.

Luckily, .NET has an assembly that has methods to serialize and deserialize JSON objects that's easy to use and already built into .NET 3.5. It's called System.Web.Extensions (it's in System.Web.Extensions.dll), and it's an important piece of the ASP.net AJAX API -- but you can use it in our .NET client just by adding a reference to to it. You'll use the JavaScriptSerializer class, which can serialize or deserialize any class marked with the [Serializable] attribute, converting it to or from JSON.

So here's the C# version of the Thing and Guy classes (both marked [Serializable]):

Thing.cs
using System;

[Serializable]
class Thing
{
     public string What { get; set; }
 
     public int Weight { get; set; }
 
     public override string ToString()
     {
          return " - A " + What + " that weighs " 
              + Weight + " pounds\r\n";
      }
}
Guy.cs
using System;
using System.Collections.Generic;

[Serializable]
class Guy
{
     public string Name { get; set; }
     public List<Thing> Stuff { get; set; }
 
     public override string ToString()
     {
          string s = "My name is " + Name + "\r\n";
          s += "I have the following stuff:\r\n";
          foreach (Thing thing in Stuff)
              s += thing;
          return s;
      }
}

And here's a C# network client that will connect to your Java GuyServer, read the string of JSON-encoded data, and deserialize it into a Guy object. The first few lines (up to the first Console.WriteLine() connect to the server on localhost (which, if you're new to networking, means the current machine). Then it just needs a call to a new JavaScriptDeserializer object's Deserialize() method to convert that data back to a Guy.

GuyClient.cs

using System;
using System.IO;
using System.Net.Sockets;
using System.Web.Script.Serialization;

class GuyClient
{
     static void Main(string[] args)
     {
          String input;
  
          using (TcpClient tcpClient = 
                  new TcpClient("localhost", 16001))
          using (NetworkStream networkStream = 
                  tcpClient.GetStream())
          using (StreamReader streamReader = 
                  new StreamReader(networkStream))
          {
               input = streamReader.ReadToEnd();
           }
  
          Console.WriteLine("Received data: " + input + "\n");
  
          JavaScriptSerializer javaScriptSerializer = 
                  new JavaScriptSerializer();
          Guy bob = javaScriptSerializer
                  .Deserialize<Guy>(input) as Guy;
          Console.WriteLine(bob.ToString());
      }
}

Build and run the code using the Microsoft C# compiler:

C:\Code>c:\WINDOWS\Microsoft.NET\Framework\v3.5\csc /reference:System.Web.Extensions.dll /out:GuyClient.exe /nologo Thing.cs Guy.cs GuyClient.cs

C:\Code>GuyClient.exe
Received data: {"name":"Mike","stuff":[{"what":"briefcase","weight":2},{"what":"dictionary","weight":3},{"what":"rock","weight":75},{"what":"rhino","weight":1850}]}

My name is Mike
I have the following stuff:
 - A briefcase that weighs 2 pounds
 - A dictionary that weighs 3 pounds
 - A rock that weighs 75 pounds
 - A rhino that weighs 1850 pounds

So what just happened? Your program connected to the server, read JSON data from it, created a new Guy object, and filled it up with data. It recognized that the JSON data contained a list called "stuff", so it used that data to populate the Guy object's stuff property -- which just happened to be a List<Thing>. Once that was done, you had a brand new Guy object full of data.

Alternately, you can build and run the code using Mono:

bash$ gmcs /reference:System.Web.Extensions /out:GuyClient.exe GuyClient.cs Thing.cs Guy.cs

bash$ mono GuyClient.exe
Received data: {"name":"Ed","stuff":[{"what":"briefcase","weight":2},{"what":"radiator","weight":25},{"what":"dog","weight":46},{"what":"dictionary","weight":3}]}

My name is Ed
I have the following stuff:
 - A briefcase that weighs 2 pounds
 - A radiator that weighs 25 pounds
 - A dog that weighs 46 pounds
 - A dictionary that weighs 3 pounds

If you're running Windows, you can run Mono-generated GuyClient.exe directly instead of using mono in the command line.

Note: If you replace "localhost" in the code with an IP address or machine name, your client will connect over the network. If you want to give that a shot, try replacing the line that creates the TcpClient with this one. It optionally replaces "localhost" with one of the command-line arguments using this: (args.Length > 0) ? args[0] : "localhost"

So it would look like this (replacing "localhost" with the code in boldface in GuyServer.cs):

using (TcpClient tcpClient = new TcpClient(
     (args.Length > 0) ? args[0] : "localhost", 16001) )

If you add the code to replace "localhost" with a command-line argument (if you specified one), you can optionally add the hostname of a machine running GuyServer to the end of the command line (after GuyClient.exe).

Take a really close look at that JSON data. Notice how there's nothing about "Guy" or "Thing" anywhere in it. That should help you start to see where the data ends and the container begins. If you called your objects Foo and Bar instead of Guy and Thing, but you kept the field names the same, your objects would happily deserialize just fine from the same JSON data.

Build a Perl version of GuyClient

Did you see all of the various JSON libraries listed at json.org? There are libraries for pretty much every language. Once you've got your data being served up as a JSON object, you can get it to any of those platforms. So let's use JSON::XS to build a new GuyClient. (Here's a tutorial that shows you how to use JSON::XS, including how to install it.)

GuyClient.pl
#!/usr/bin/perl

use IO::Socket::INET;
use JSON::XS;

$socket = new IO::Socket::INET->new(
     PeerPort=>16001,
     PeerAddr=>'localhost');

$socket->recv($jsontext,32768);

my $guy = JSON::XS->new->decode ($jsontext);

print "My name is $guy->{name} and I have the following stuff:\n";
@stuff = @{$guy->{stuff}};
foreach $thing(@stuff) { 
    print "A $thing->{what} that weighs $thing->{weight} pounds\n";
}

Here's what it looks like when you run your new Perl client and it connects to your Java server:

bash$ ./GuyClient.pl
My name is Bob and I have the following stuff:
A radiator that weighs 25 pounds
A dog that weighs 46 pounds
A dictionary that weighs 3 pounds

Take a step back and have a look at what you built. You've got a server written in Java that creates objects and serves them up to clients in C# and Perl. You can run your Java server on one machine and connect to it from a C# client on another machine. And you did it all with very little code. That's pretty neat!

So what did we learn?

The barrier between platforms isn't hard to breach. That may be surprising to you, or it may be a surprise that it's even an issue. We started to see the line between data and container, and learned how the way the data's encoded is completely independent of the data. And hopefully we learned how JSON works, and saw it used in three different languages.

And, just as importantly, what didn't we learn?

Congratulations. You now know just enough to be dangerous.

The examples here just scratch the surface of cross-platform server architecture. It's really easy to look at our client and server and think that we've now got all the tools we need to build larger systems.

There's a reason I gave a lot of warnings about how these examples aren't templates for building clients and servers. Architecture is about a lot more than just getting different pieces into a single system. It's about building systems that work well. I wrote this post on Building Better Software that talks about a few of the things that software designers and architects need to think about, including performance, flexibility, and reliability.

With systems where clients and servers communicate over a network, there are other really important things to think about, too. Security is always a challenge. (Can the communication be intercepted or eavesdropped? Can malicious clients authenticate improperly with your server, or cause damage to the system the server lives on?) There's scalability. (What happens if you have more clients than your server can handle?) We didn't talk about handling errors or exceptions, malformed requests or responses -- architects call that "robustness" -- at all. And there are certainly ways for clients and servers to communicate that take fewer bytes (like Google Protocol Buffers).

Where to go next...

So we definitely did not learn how to build a server. That's definitely not a pattern you want to use on a real project. But you have a good learning platform to expand your knowledge, and that's what this is really about.

But it's a great way to learn about data transport. If you're interested in learning more, the next step is to experiment: try more complex data types, try sending data back and forth between the client and the server. Can you figure out How would you keep track of the state of your client between connections? If you're feeling ambitious, can you figure out how to use Google Protocol Buffers instead of JSON?

If you come up with something cool, let us know about it in a comment!

You can read more of Andrew's posts at Building Better Software.


You might also be interested in:

7 Comments

This is a very nice tutorial. I like the way you kept things short, simple yet complete. It definitely gave me a better understanding of architecture and connecting code on disparate systems.

Thanks!

It's a good article. Just like David says above,I like the way you kept things short, simple yet complete. You are really a good teacher!

Very informative and detailed. Thanks.

Definately like your style. Sealed the deal on a couple of Ebooks (that I would probably have bought anyway). Way too many texts (even good ones) fail to even acknowledge the existance of data transaction partners outside of the scope of their immediate (and often inbred) families.

Great article. Big thanks :)

Hi .. My names hakan .. Very informative and detailed. Thanks.

This document help me too much. Thank you very much.
Asma Gergi Metal Tavan Sistemleri

News Topics

Recommended for You

Got a Question?