我需要将 .NET 中生成的 Guid 传递给 Java 应用程序。我使用
Guid.ToByteArray()
将其作为 byte[]
存储在磁盘上,然后将其读入 Java 并将其转换为 UUID。为此,我复制了 UUID 的(私有)构造函数的实现,该构造函数采用 byte[]
:
private UUID(byte[] data) {
long msb = 0;
long lsb = 0;
assert data.length == 16;
for (int i=0; i<8; i++)
msb = (msb << 8) | (data[i] & 0xff);
for (int i=8; i<16; i++)
lsb = (lsb << 8) | (data[i] & 0xff);
this.mostSigBits = msb;
this.leastSigBits = lsb;
}
但是,当我使用
toString()
检查 UUID 时,Java UUID 与 .NET Guid 不同。
例如,.NET Guid
888794c2-65ce-4de1-aa15-75a11342bc63
变成Java UUID
c2948788-ce65-e14d-aa15-75a11342bc63
看起来前三组的字节顺序颠倒了,而后两组的顺序是相同的。
由于我希望 Guid 和 UUID 的
toString()
产生相同的结果,有谁知道我应该如何正确地将 .NET Guid 读入 Java UUID?
编辑:澄清一下,实施不是我自己的。它是
java.util.UUID
类的私有构造函数,它采用 byte[]
,我将其复制以用于将 byte[] 从磁盘读取到 UUID。
我不想使用字符串来存储指南,因为我存储了很多指南,这似乎浪费空间。
Russell Troywest 的链接至少澄清了为什么 Guid 的前几组结果相反,而后半部分保持相同的顺序。问题是,我可以依赖 .NET always 以相同的顺序生成这些字节吗?
您不能将 .Net Guid 存储为字符串并将其读入 Java 中吗?这样你就不需要担心字节顺序或任何事情。
如果不是,那么这解释了 C# 中字节是如何布局的
编辑 2017-08-30:每个评论交换了数组元素 6 和 7。
我必须在 C# 应用程序中从 MySQL(存储为二进制 (16))读取和写入 Guid,但该数据库也由 Java 应用程序使用。以下是我用于在 .NET 小端字节顺序和 Java 大端字节顺序之间转换的扩展方法:
public static class GuidExtensions
{
/// <summary>
/// A CLSCompliant method to convert a Java big-endian Guid to a .NET
/// little-endian Guid.
/// The Guid Constructor (UInt32, UInt16, UInt16, Byte, Byte, Byte, Byte,
/// Byte, Byte, Byte, Byte) is not CLSCompliant.
/// </summary>
[CLSCompliant(true)]
public static Guid ToLittleEndian(this Guid javaGuid) {
byte[] net = new byte[16];
byte[] java = javaGuid.ToByteArray();
for (int i = 8; i < 16; i++) {
net[i] = java[i];
}
net[3] = java[0];
net[2] = java[1];
net[1] = java[2];
net[0] = java[3];
net[5] = java[4];
net[4] = java[5];
net[6] = java[7];
net[7] = java[6];
return new Guid(net);
}
/// <summary>
/// Converts little-endian .NET guids to big-endian Java guids:
/// </summary>
[CLSCompliant(true)]
public static Guid ToBigEndian(this Guid netGuid) {
byte[] java = new byte[16];
byte[] net = netGuid.ToByteArray();
for (int i = 8; i < 16; i++) {
java[i] = net[i];
}
java[0] = net[3];
java[1] = net[2];
java[2] = net[1];
java[3] = net[0];
java[4] = net[5];
java[5] = net[4];
java[6] = net[7];
java[7] = net[6];
return new Guid(java);
}
}
如前所述,.NET 中 GUID 的二进制编码的前三组中的字节以小端顺序(相反)放置 - 请参阅Guid.ToByteArray 方法。要从中创建
java.util.UUID
,您可以使用以下代码:
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.UUID;
public UUID toUUID(byte[] binaryEncoding) {
ByteBuffer source = ByteBuffer.wrap(binaryEncoding);
ByteBuffer target = ByteBuffer.allocate(16).
order(ByteOrder.LITTLE_ENDIAN).
putInt(source.getInt()).
putShort(source.getShort()).
putShort(source.getShort()).
order(ByteOrder.BIG_ENDIAN).
putLong(source.getLong());
target.rewind();
return new UUID(target.getLong(), target.getLong());
}
根据您的编辑,不,您不能始终依赖于以相同顺序生成的字节。运行时决定字节顺序。然而,出于这个原因,C# 确实提供了
BitConverter.isLittleEndian
。
我知道你无法改变 Java 实现的字节顺序和位移位。但您可以在存储之后、将它们发送到 Java 之前在 C# 端移动这些位。
更新:
编辑: 实际上,您可能可以指望它在第一个字节块的布局中始终是小尾数,但从技术上讲您不能。
GUID.toByteArray 在 C# 中相当奇怪。前半部分为小端,后半部分为大端。
此页面上的评论指出了这一事实: http://msdn.microsoft.com/en-us/library/system.guid.tobytearray.aspx
返回的字节数组中的字节顺序与 Guid 值的字符串表示形式不同。开始的四字节组和接下来的两个两字节组的顺序相反,而最后一个两字节组和结束的六字节组的顺序相同。
我认为你的问题是.NET是小端字节序,而JAVA是大端字节序,所以当你从JAVA应用程序读取由C#应用程序编写的128位整数(GUID)时,你必须进行de转换从小端到大端。
DotNetGuid1Codec
和 DotNetGuid4Codec
可以将 UUID 编码为 .Net Guid。
// Convert time-based (version 1) to .Net Guid
UuidCodec<UUID> codec = new DotNetGuid1Codec();
UUID guid = codec.encode(timeUuid);
// Convert random-based (version 4) to .Net Guid
UuidCodec<UUID> codec = new DotNetGuid4Codec();
UUID guid = codec.encode(randomUuid);
参见:uuid-creator
此代码对我有用。
var msb: Long = 0
var lsb: Long = 0
for(i <- Seq(3, 2, 1, 0, 5, 4, 7, 6)) {
msb = (msb << 8) | (data(i) & 0xFF)
}
for(i <- 8 until 16) {
lsb = (lsb << 8) | (data(i) & 0xFF)
}
new UUID(msb, lsb)
public static Guid ToGuid(this JavaUUID self)
{
var vec = Vector128.Shuffle(
Vector128.Create(self.MostSigBits, self.LeastSigBits).AsByte(),
Vector128.Create((byte)4, 5, 6, 7, 2, 3, 0, 1, 15, 14, 13, 12, 11, 10, 9, 8)
);
return Unsafe.As<Vector128<byte>, Guid>(ref vec);
}