Question

We have .NET protobuf webservices, one of them returns a single GUID in response, like this:

return newItemGuid;

How to read this value from Android code? We receive a response of 20 bytes, and passing them to either parseFrom(byte[]) or parseFrom(InputStream) doesn't work - returned value is empty Guid.

How to properly parse such response?

Here are bytes from response:

[10, 18, 9, 73, -7, 96, 69, -115, -33, 29, 68, 17, -107, -110, -92, 46, -100, -113, -10, -64]

And GUID should be: 4c6640a7-c955-4c34-b2d7-7b470cba4a9c

Was it helpful?

Solution

OK, I owe you an apology here; basically, the v1 version of protobuf-net used Guid.ToByteArray, which - if I had to give it a name - I would have to call "crazy-endian" - and it uses what is perhaps a bad design choice of layouts. To illustrate the "crazy-endian":

var guid = new Guid("00112233445566778899AABBCCDDEEFF");
var msBlob = guid.ToByteArray();
var msHex = BitConverter.ToString(msBlob);

Obviously... msHex is the string:

33-22-11-00-55-44-77-66-88-99-AA-BB-CC-DD-EE-FF

You should be able to see how each input / output byte maps in the above; I mean, who wouldn't choose that as the obvious choice of Guid.ToByteArray? Just... sigh.

OK, so... that's odd... but: this unusual order wasn't noticed when v1 chose to use ToByteArray. Although, IIRC it was noticed how odd it was when v2 needed to prove compatibility (v2 initially made the assumption that it was implemented in a "sane" way, and immediately failed all the tests).

Now, protobuf-net uses the layout defined in bcl.proto, specifically:

message Guid {
  optional fixed64 lo = 1; // the first 8 bytes of the guid
  optional fixed64 hi = 2; // the second 8 bytes of the guid
}

So - a sub-message containing two fields, a lo and a hi. The field headers for field 1 fixed64 and field 2 fixed64 are 09 and 11 respectively, so we should expect:

09-33-22-11-00-55-44-77-66-11-88-99-AA-BB-CC-DD-EE-FF

which is... yeah, that's kinda sucky. A poor design choice. If I could go back and re-make that decision, I would have made it just a bytes of expected length 16, and I would have fixed the endianness. Well, hindsight is great. It is, however, hugely problematic to revert a poor decision that was made many years ago without impacting existing code.

So that's 18 bytes accounted for. The final 2 bytes are almost certainly a leading 0A-12, which means "field 1, length delimited: 18 bytes".

So; you have options:

  • decode as a lo/hi pair, and account for the crazy-endianness (for which I have nothing to offer but apologies)
  • change the .NET code to advertise the value as a byte[] instead
  • there's also a case to be made for a feature request to implement Guid in a sane way out of the box; I agree that the existing implementation is frankly: bad - it would have to be opt-in, though, to avoid regressions
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top