views:

124

answers:

4

I'm writing a .NET application that will make an RPC call to a Java application (via a message queue). The data sent in both directions will be large arrays of floating-point numbers. What is the best way to serialize them to send them across the wire? I'd like something more compact than text, but architecture-independent as the server may not be an x86 machine. The Java application can be changed as needed.

+1  A: 

JSON comes to mind. Or it's binary counterpart, BSON. Both have implementations in Java and .NET

Robert Harvey
BSON looked promising, but there doesn't seem to be much client support. The "Java client" link just goes to a single source file in some DB driver. WTF?
Evgeny
A: 

Text is really the only platform independent way to represent floating point numbers, and its certainly the easiest to do. If you are concerned about message size, you could always GZip your messages.

luke
A: 

I'm currently reading about Protocol Buffers and they look promising. There is an official Google implementation for Java and a C# port of it by none other than Jon Skeet.

Evgeny
+3  A: 

Java numeric primitives actually are stored (in the JVM) and written (via jnava.io.DataOutputStream and java.nio.ByteBuffer) in network order and the floating point values are IEEE standard. They are directly interchangeable with C#/.NET. Well, if .NET provided network byte order to read doubles (see below for code).

So, you can send and receive any primitive using the two classes mentioned here (and the input counterparts) as long as the .NET side is reading/writing in network byte order as you should always use.

Java side for example:

// Assume the following somewhere in your class
Socket socket;
DataOutputStream out = new DataOutputStream(socket.getOutputStream());

// Send a double
out.writeDouble(doubleValue);

C# side to retrieve the value:

Stream stream = new NetworkStream(socket, FileAccess.ReadWrite, true);
BinaryReader reader = new BinaryReader(stream, Encoding.UTF8);   

// Read double from the stream
long v = IPAddress.NetworkToHostOrder(reader.ReadInt64());   
double doubleValue = BitConverter.Int64BitsToDouble(v);

For writing you do the opposite, C# has to write in network byte order.

Stream stream = new NetworkStream(socket, FileAccess.ReadWrite, true);
BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8);

// Write double to the stream
long v = BitConverter.DoubleToInt64Bits(doubleValue);
writer.Write(IPAddress.HostToNetworkOrder(v));

And then on the Java side to read these back in:

// Some where in your class
Socket socket;
DataInputStream in = new DataInputStream(socket.getInputStream());

// To read the double
double doubleValue = in.readDouble();

The C# IPAddress class provides network byte order reading/writing methods for all primitives except double and float, but as in my example you can pass through through either a 32-bit or 64-bit int respectively.

Kevin Brock
You know, I might just go with this simple approach. .NET also has `System.IO.BinaryWriter` and `System.IO.BinaryReader` which handle the serialization. (I won't be using socket classes directly.)
Evgeny
C# is .NET; the `BinaryReader` and `BinaryWriter` classes in my examples are .NET; these don't exist in Java.
Kevin Brock
If you want to put the values in byte arrays and operate on those separately, you may want to use Java's `java.nio.ByteBuffer`. See my answer here: http://stackoverflow.com/questions/2868885/representing-a-number-in-a-byte-array-java-programming/2870823#2870823
Kevin Brock