Как читать .NET Guid в Java UUID
Мне нужно передать Guid, который был сгенерирован в .NET для Java-приложения. Я использую Guid.ToByteArray()
для хранения его на диске как byte[]
, затем прочитайте его в Java и преобразуйте его в UUID. Для этого я скопировал реализацию (private) конструктора 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;
}
Однако, когда я проверяю UUID с помощью toString()
, UUID Java отличается от .NET Guid.
Например,.NET Guid
888794c2-65ce-4de1-aa15-75a11342bc63
превращается в Java UUID
c2948788-ce65-e14d-aa15-75a11342bc63
Кажется, что порядок байтов первых трех групп отменен, а порядок в двух последних группах одинаковый.
Поскольку я ожидал бы, что toString()
как Guid, так и UUID даст тот же результат, кто-нибудь знает, как я должен правильно прочитать .NET Guid в Java UUID?
Изменить: Чтобы пояснить, реализация не моя. Это частный конструктор класса java.util.UUID
, который принимает byte[]
, который я скопировал, чтобы использовать для чтения байта [] с диска в UUID.
Я не хочу использовать строки для хранения Гидов, поскольку я храню их много, и это кажется пустой тратой пространства.
Ссылка Russell Troywest, по крайней мере, разъясняет, почему первые пары групп Guid выходят в обратном направлении, а вторая половина остается в том же порядке. Вопрос в том, могу ли я зависеть от .NET всегда, генерируя эти байты в том же порядке?
Ответы
Ответ 1
В ответ на ваше редактирование нет, вы не можете последовательно зависеть от создаваемых байтов в том же порядке. Время выполнения определяет конечность. Однако С# предлагает BitConverter.isLittleEndian
по этой причине.
Я знаю, что вы не можете изменить сущность реализации Java и смещение битов. Но вы можете сдвинуть бит на конец С# после сохранения и перед отправкой их на Java.
Update:
Статья MSDN на IsLittleEndian
Изменить:
Чтобы быть практичным, вы можете ВЕРОЯТНО рассчитывать на то, что он всегда мало ориентирован по своему расположению первого фрагмента байтов, но технически вы не можете.
Ответ 2
Не могли бы вы просто сохранить .Net Guid в виде строки и прочитать ее на Java? Таким образом вам не нужно беспокоиться о порядке байтов или что-то еще.
Если нет, то это объясняет, как байты выложены в С#
http://msdn.microsoft.com/en-us/library/fx22893a.aspx
Ответ 3
Изменить 2017-08-30. Поменял элементы массива 6 и 7 на каждый комментарий.
Мне нужно читать и писать гиды из/в MySQL (хранящиеся как двоичные (16)) в приложении С#, но база данных также используется Java-приложениями. Вот методы расширения, которые я использую для преобразования между порядком байтов большого и младшего порядка в формате .NET:
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);
}
}
Ответ 4
GUID.toByteArray довольно странно в С#. Первая половина находится в малой степени, а вторая половина - в большой эндемии.
Комментарий на этой странице отмечает этот факт:
http://msdn.microsoft.com/en-us/library/system.guid.tobytearray.aspx
порядок байтов в возвращаемом массиве байтов отличается от строкового представления значения Guid. Порядок начальной четырехбайтовой группы и двух следующих двухбайтовых групп меняется на противоположные, тогда как порядок последней двухбайтовой группы и закрывающей шестибайтовой группы один и тот же.
Ответ 5
Как уже отмечалось, двоичное кодирование GUID в .NET имеет байты в первых трех группах, расположенные в порядке с прямым порядком байтов (обратный порядок) - см. Метод 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());
}
Ответ 6
Я думаю, что ваша проблема здесь в том, что .NET - малодушная, но JAVA - это big-endian, поэтому, когда вы читаете 128-битное целое число (GUID), написанное с помощью приложения С# из приложения JAVA вам нужно выполнить преобразование из little-endian в big-endian.
Ответ 7
Этот код работает для меня.
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)
Ответ 8
В UuidUtil
есть метод, который делает это, если кому-то еще это нужно.
UUID uuid = UuidUtil.fromMssqlGuidToUuid(guid);
Другой метод делает обратное.
UUID guid = UuidUtil.fromUuidToMssqlGuid(uuid);
https://github.com/f4b6a3/uuid-creator