How to Read a .Net Guid into a Java Uuid

How to read a .NET Guid into a Java UUID

In response to your edit, no, you cannot consistently depend on the bytes being generated in the same order. The runtime determines the endianness. C# does however offer BitConverter.isLittleEndian for this very reason.

I know you can't change the endianness of the Java implementation and the bit shifting. But you can shift the bits on the C# end after storing and before sending them to Java.

Update:

MSDN Article on IsLittleEndian

Edit:
To be practical, you can PROBABLY count on it always being little endian in its layout of the first chunk of bytes, but technically you can't.

Transforming a Java UUID object to a .NET GUID string

Guid is represented by 16 bytes. For various reasons, both Java and .NET do not just print those bytes in order when you call toString. For example, if we look at base-64 encoded guid from your question:

GAElHwsix0a41iIsvQMT8A==

In hex form it will look like this:

18-01-25-1f-0b-22-c7-46-b8-d6-22-2c-bd-03-13-f0

Java toString produces this (if we format as above):

46-c7-22-0b-1f-25-01-18-f0-13-03-bd-2c-22-d6-b8

.NET ToString produces this:

1f-25-01-18-22-0b-46-c7-b8-d6-22-2c-bd-03-13-f0

If you look at this for some time - you will notice that both java and .NET strings represent the same 16 bytes, but positions of those bytes in output string are different. So to convert from java representation to .NET you just need to reorder them. Sample code (I don't know java, so probably it could be done in a better way, but still should achieve the desired result):

static String GetStringFromUuid (java.util.UUID myUuid){
byte[] bytes = new byte[16];
// convert uuid to byte array
ByteBuffer bb = ByteBuffer.wrap(bytes);
bb.putLong(myUuid.getMostSignificantBits());
bb.putLong(myUuid.getLeastSignificantBits());
// reorder
return String.format("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
bytes[4],bytes[5],bytes[6],bytes[7],
bytes[2],bytes[3],bytes[0],bytes[1],
bytes[15],bytes[14],bytes[13],bytes[12],
bytes[11],bytes[10],bytes[9],bytes[8]);
}

How to use .net GUID in JAVA UUID as primary key of mongodb collection

I have found solution. Hope it will help someone else.
So I took the code of util js file for CSUUID convertion to UUID in robomongo and rewrite it to JAVA.

private String HexToBase64(String hex) {
char[] base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
String base64 = "";
int group;
for (int i = 0; i < 30; i += 6) {
group = Integer.parseInt(mySubString(hex, i, 6), 16);
base64 += base64Digits[(group >> 18) & 0x3f];
base64 += base64Digits[(group >> 12) & 0x3f];
base64 += base64Digits[(group >> 6) & 0x3f];
base64 += base64Digits[group & 0x3f];
}
group = Integer.parseInt(mySubString(hex, 30, 2), 16);
base64 += base64Digits[(group >> 2) & 0x3f];
base64 += base64Digits[(group << 4) & 0x3f];
base64 += "==";
return base64;
}
private String mySubString(String myString, int start, int length) {
return myString.substring(start, Math.min(start + length, myString.length()));
}
private String CSUUID(String csuuid) {
String hex = csuuid.replaceAll("[{}-]", ""); // remove extra characters
String a = mySubString(hex, 6, 2) + mySubString(hex, 4, 2) + mySubString(hex, 2, 2) + mySubString(hex, 0, 2);
String b = mySubString(hex, 10, 2) + mySubString(hex, 8, 2);
String c = mySubString(hex, 14, 2) + mySubString(hex, 12, 2);
String d = mySubString(hex, 16, 16);
hex = a + b + c + d;
String base64 = HexToBase64(hex);
//return base64.getBytes(StandardCharsets.UTF_8);
return base64;
}

Now to perform query by CSUUID need to perform such query:

var user = _mongo.findOne(Query.query(Criteria.where("_id").is(new Binary(Bytes.B_UUID, Base64.getDecoder().decode(CSUUID(id.toString()))))), User.class);  

Thanks!

Is there any way to generate a UUID in Java that is identical to that of the one generated in C#?

TL;DR

If you want your C# and your Java to act exactly the same way (and you are happy with the existing C# behaviour), you'll need to manually re-order some of the bytes in uuid_bytes (i.e. swap some of the entries you identified as out of order).

Additionally, you should not use:

UUID.nameUUIDFromBytes(to_encode.trim().getBytes())

But instead use:

public static String getGuidFromByteArray(byte[] bytes) {
ByteBuffer bb = ByteBuffer.wrap(bytes);
long high = bb.getLong();
long low = bb.getLong();
UUID uuid = new UUID(high, low);
return uuid.toString();
}

Shamelessly stolen from https://stackoverflow.com/a/24409153/34092 :)

Additional Background

In case you weren't aware, when dealing with C#'s GUIDs:

Note that the order of bytes in the returned byte array is different
from the string representation of a Guid value. The order of the
beginning four-byte group and the next two two-byte groups is
reversed, whereas the order of the last two-byte group and the closing
six-byte group is the same. The example provides an illustration.

And:

The order of hexadecimal strings returned by the ToString method
depends on whether the computer architecture is little-endian or
big-endian.

In your C#, rather than using:

Console.WriteLine("Guid: {0}", guid);

you may want to consider using:

Console.WriteLine(BitConverter.ToString(guid.ToByteArray()));

Your existing code calls ToString behind the scenes. Alas, ToString and ToByteArray do not return the bytes in the same order.

What is the equivalent to .Net Guid.ToByteArray() in Java

The near-equivalent class in Java is java.util.UUID. However, as you've noticed, the two do not give the same byte arrays. But if you execute the following and look at the array given by Java versus the array given by .NET:

import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.UUID;

public class Main {

// expected from your question
private static final int[] EXPECTED_BYTES = {
185, 242, 54, 152, 140, 186, 166, 66, 184, 132, 46, 158, 237, 159, 185, 90
};

public static void main(String[] args) {
UUID uuid = UUID.fromString("9836f2b9-ba8c-42a6-b884-2e9eed9fb95a");

byte[] array = toByteArray(uuid);

System.out.println("EXPECTED: " + Arrays.toString(EXPECTED_BYTES));
System.out.println("ACTUAL : " + Arrays.toString(toUnsignedInts(array)));
}

private static byte[] toByteArray(UUID uuid) {
return ByteBuffer.allocate(16)
.putLong(uuid.getMostSignificantBits())
.putLong(uuid.getLeastSignificantBits())
.array();
}

// for visual purposes only
private static int[] toUnsignedInts(byte[] array) {
int[] result = new int[array.length];
for (int i = 0; i < array.length; i++) {
result[i] = Byte.toUnsignedInt(array[i]);
}
return result;
}
}

And the output:

EXPECTED: [185, 242, 54, 152, 140, 186, 166, 66, 184, 132, 46, 158, 237, 159, 185, 90]
ACTUAL : [152, 54, 242, 185, 186, 140, 66, 166, 184, 132, 46, 158, 237, 159, 185, 90]

You'll see the arrays are almost equal, it's just the order of some bytes don't match. The last eight bytes (i.e. the least significant bits) all match, but the first four bytes are reversed, the next two bytes are reversed, and so are the next two bytes. To see it visually:

EXPECTED: [185, 242, 54, 152, 140, 186, 166, 66, 184, 132, 46, 158, 237, 159, 185, 90]
ACTUAL : [152, 54, 242, 185, 186, 140, 66, 166, 184, 132, 46, 158, 237, 159, 185, 90]
|---------------| |------| |-----|

I don't know enough to explain why this difference exists, but this comment on an answer to a question you linked to says:

See also [Universally unique identifier - Wikipedia] "Many systems encode the UUID entirely in a big-endian format." "Other systems, notably Microsoft's marshalling of UUIDs in their COM/OLE libraries, use a mixed-endian format, whereby the first three components of the UUID are little-endian, and the last two are big-endian." – Denis Dec 20 '19 at 13:06

The answer that comment is on gives a solution to your problem, which you've included in your question. That solution simply swaps bytes around to get the desired effect. Here's another solution that doesn't involve creating a copy array:

private static byte[] toByteArray(UUID uuid) {
long mostSigBits = uuid.getMostSignificantBits();
return ByteBuffer.allocate(16)
.order(ByteOrder.LITTLE_ENDIAN)
.putInt((int) (mostSigBits >> 32))
.putShort((short) (((int) mostSigBits) >> 16))
.putShort((short) mostSigBits)
.order(ByteOrder.BIG_ENDIAN)
.putLong(uuid.getLeastSignificantBits())
.array();
}

Note: I'm not very comfortable with bit-shifting, so there may be a more succinct way of accomplishing the above that I couldn't think of.

Which gives the following output:

EXPECTED: [185, 242, 54, 152, 140, 186, 166, 66, 184, 132, 46, 158, 237, 159, 185, 90]
ACTUAL : [185, 242, 54, 152, 140, 186, 166, 66, 184, 132, 46, 158, 237, 159, 185, 90]

Warning: Unfortunately, I'm not sure you can rely on either workaround giving the correct bytes 100% of the time.

  • How to read a .NET Guid into a Java UUID
  • Is there any difference between a GUID and a UUID?

Reading Java UUID (from DB) in C#

If you've already got it as a byte array, then just call new Guid(bytes) to get a Guid; you can call ToString on that to convert it to a string if you particularly need to. (I'd suggest leaving it as a Guid though other than for diagnostics.)

Guid to Base64 in Java

The structure is a bit different, but swapping some bytes in the first part of the byte array fixes your problem.

java.util.Base64.Encoder encoder= Base64.getEncoder();
UUID uuid = UUID.fromString("be9f1bb6-5c8e-407d-85a3-d5ef31f21b4d");
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());

byte[] uuid_bytes = bb.array();
byte[] guid_bytes = Arrays.copyOf(uuid_bytes,uuid_bytes.length);

guid_bytes[0] = uuid_bytes[3];
guid_bytes[1] = uuid_bytes[2];
guid_bytes[2] = uuid_bytes[1];
guid_bytes[3] = uuid_bytes[0];
guid_bytes[4] = uuid_bytes[5];
guid_bytes[5] = uuid_bytes[4];
guid_bytes[6] = uuid_bytes[7];
guid_bytes[7] = uuid_bytes[6];

String result = encoder.encodeToString(guid_bytes);


Related Topics



Leave a reply



Submit