有没有办法在Java中生成与C#生成的UUID相同的UUID?

10

我正在将一份C#脚本移植到Spark(Scala)中,但在Scala中生成UUID与在C#中生成GUID存在问题。

是否有办法在Java中生成与C#中生成的UUID相同的UUID?

我通过从字符串的MD5哈希值创建Guid来生成数据库的主键。最终,我希望能够生成Java / Scala中与C#脚本中相匹配的UUID,以便使用C#实现哈希处理的现有数据无需重新哈希。

需要移植的C#代码:

String ex = "Hello World";
Console.WriteLine("String to Hash: {0}", ex);
byte[] md5 = GetMD5Hash(ex);
Console.WriteLine("Hash: {0}", BitConverter.ToString(md5));
Guid guid = new Guid(md5);
Console.WriteLine("Guid: {0}", guid);

private static byte[] GetMD5Hash(params object[] values) {
  using (MD5 md5 = MD5.Create())
    return md5.ComputeHash(Encoding.UTF8.GetBytes(s));
} 

Scala 移植代码:

val to_encode = "Hello World"
val md5hash = MessageDigest.getInstance("MD5")
 .digest(to_encode.trim().getBytes())
val md5string = md5hash.map("%02x-".format(_)).mkString
val uuid_bytes = UUID.nameUUIDFromBytes(to_encode.trim().getBytes())
printf("String to encode: %s\n", to_encode)
printf("MD5: %s\n", md5string)
printf("UUID: %s\n", uuid_bytes.toString)

C#的结果:

  • 需要哈希的字符串:Hello World
  • MD5: B1-0A-8D-B1-64-E0-75-41-05-B7-A9-9B-E7-2E-3F-E5
  • GUID: b18d0ab1-e064-4175-05b7-a99be72e3fe5

Scala的结果:

  • 需要哈希的字符串:Hello World
  • MD5: b10a8db164e0754105b7a99be72e3fe5
  • UUID: b10a8db1-64e0-3541-85b7-a99be72e3fe5

可以匹配的内容:

  • MD5哈希(GUID和UUID都是基于此)匹配。

不匹配的内容:

  • 前三个字段在C#中的字节序已切换(橙色)
    • C#的GUID选择前三个字段(4、2、2)的本机字节顺序,这在这种情况下是小端字节序,并且将最后一个字段(8)设置为大端字节序,而Java的UUID对所有四个字段都使用大端字节顺序;这解释了C#中前三个字段的字节顺序。
  • 第四个和第五个字节不同(红色)
    • Java在6-7位上交换以表示UUID的版本和变体,这可能解释了第4和第5个字节中的差异。 这似乎是难以克服的障碍。
  • 我知道Java使用有符号字节,而C#使用无符号字节;这也可能相关。

除了操作字节,还有其他修复此问题的方法吗?


1
@JoeC 请仔细阅读整个问题,我一开始也只看了标题,但如果你读完整个问题就会明白,他是在基于MD5哈希构建GUID。 - Gusman
顺便提一下,除非它们是特定的连续序列,否则数据库通常不适用UUID主键。 - Crowcoder
1
你确定在Scala示例中是UUID.nameUUIDFromBytes(to_encode.trim().getBytes())吗?在C#示例中,您使用哈希作为Guid的输入。 - Bernhard Hiller
在C#方面,相关代码调用自定义Guid构造函数,该构造函数使用一个byte[],在这种情况下是MD5哈希。由于我想在Scala方面模拟这种行为,所以我想使用使用MD5哈希的版本3 UUID会是最接近的东西。你有其他更好的方法吗? - Ari Krumbein
我会更新我的回答并包含它@AriKrumbein - 请随意点赞并接受它。 - mjwills
显示剩余3条评论
1个回答

7

简述

如果你想让你的C#和Java表现一致(并且你对现有的C#行为感到满意),你需要手动重新排列uuid_bytes中的一些字节(即交换您确定为错误顺序的某些条目)。

此外,您不应使用:

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

但是请使用以下写法:
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();
}

以下内容来自https://dev59.com/hGAf5IYBdhLWcg3wqELp#24409153

背景介绍

如果您不知道,在处理 C# 的 GUIDs 时:

请注意,返回的字节数组中的字节顺序与 Guid 值的字符串表示形式不同。开头的四个字节组和接下来的两个双字节组的顺序是相反的,而最后的两个双字节组和结束的六个字节组的顺序是相同的。本示例提供了说明。

并且:

ToString 方法返回的十六进制字符串的顺序取决于计算机体系结构是小端还是大端。

在您的 C# 中,不要使用:

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

你可能想考虑使用以下内容:
Console.WriteLine(BitConverter.ToString(guid.ToByteArray()));

您现有的代码在幕后调用ToString。然而,ToStringToByteArray返回的字节顺序并不相同,请参见此处


我应该补充一下:C# 是一个我(通常)无法修改的系统。感谢您的帮助。如果您对第二个问题有任何建议,我将不胜感激。 - Ari Krumbein
谢谢。如果您解决了版本和变体位问题,请告诉我。 - Ari Krumbein
这很可能是因为在C#和Scala中,你使用了两种不同的方式生成UUID。这个链接 https://gist.github.com/jeffjohnson9046/c663dd22bbe6bb0b3f5e 有帮助吗? - mjwills
FYI @mjwills,这里的警告是由于缺少版本和变体位,我相当确定这些不是有效的Java UUIDs,这对我来说并不重要,但对其他人可能很重要。你可能需要在你的回答中包含这一点。 - Ari Krumbein
你为什么认为它们不是有效的Java UUID?说它们与Java生成的格式不完全相同可能是正确的。但这与说它们无效是不同的。 - mjwills

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接