Как прочитать .NET Guid в Java UUID

Мне нужно передать Guid, созданный в .NET, в приложение 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;
}

Однако, когда я проверяю UUID с помощью toString(), UUID Java отличается от .NET Guid.

Например, руководство по .NET.

888794c2-65ce-4de1-aa15-75a11342bc63

превращается в Java UUID

c2948788-ce65-e14d-aa15-75a11342bc63

Кажется, что порядок байтов в первых трех группах обратный, в то время как порядок в последних двух группах тот же.

Поскольку я ожидаю, что toString() как Guid, так и UUID дадут один и тот же результат, кто-нибудь знает, как мне правильно прочитать .NET Guid в Java UUID?

Редактировать. Чтобы уточнить, это не моя реализация. Это закрытый конструктор класса java.util.UUID, который принимает byte[], который я скопировал для использования с целью чтения byte[] с диска в UUID.

Я не хочу использовать строки для хранения гуидов, так как я храню их много, и это кажется пустой тратой места.

Ссылка Рассела Тройвеста, по крайней мере, проясняет, почему первая пара групп Гида выходит в обратном порядке, а вторая половина остается в том же порядке. Вопрос в том, могу ли я полагаться на то, что .NET всегда генерирует эти байты в одном и том же порядке?


person The Slicer    schedule 21.04.2011    source источник
comment
Похоже, вы неправильно переключаете биты. Зачем пытаться быть милым с ним? Сначала прочитайте байты и выполните соответствующие назначения (сначала используя индекс), а затем используйте оператор сдвига для оптимизации (при необходимости). Суть в том, чтобы код был понятным.   -  person casperOne    schedule 21.04.2011
comment
Java хранит данные строго с обратным порядком байтов, в то время как C# не определяет порядок следования байтов, но ОБЫЧНО хранит данные как Little Endian. Как сказал @casperOne, вы переключаетесь не в ту сторону.   -  person Doug Stephen    schedule 21.04.2011
comment
Я пытался перепроектировать структуру, использующую эту конструкцию. Я смотрел на странный битовый сдвиг более двух часов, пока не нашел эту тему.   -  person Jouke Waleson    schedule 24.11.2012


Ответы (8)


В ответ на ваше редактирование: нет, вы не можете постоянно зависеть от того, что байты генерируются в одном и том же порядке. Среда выполнения определяет порядок следования байтов. Однако С# предлагает BitConverter.isLittleEndian именно по этой причине.

Я знаю, что вы не можете изменить порядок байтов реализации Java и сдвиг битов. Но вы можете сдвинуть биты на конце C# после сохранения и перед отправкой их в Java.

Обновлять:

Статья MSDN об IsLittleEndian

Редактировать: чтобы быть практичным, вы, ВОЗМОЖНО, можете рассчитывать на то, что он всегда будет прямым порядком байтов в его расположении первого фрагмента байтов, но технически вы не можете.

person Doug Stephen    schedule 21.04.2011

Не могли бы вы просто сохранить .Net Guid в виде строки и прочитать ее в Java? Таким образом, вам не нужно беспокоиться о порядке байтов или чем-то еще.

Если нет, то это объясняет, как байты расположены в С#.

http://msdn.microsoft.com/en-us/library/fx22893a.aspx

person Russell Troywest    schedule 21.04.2011

Edit 2017-08-30: элементы массива 6 и 7 заменены местами для каждого комментария.

Мне нужно читать и записывать Guids из/в MySQL (хранящиеся как двоичные файлы (16)) в приложении C#, но база данных также используется приложениями 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);
    }
}
person Community    schedule 14.04.2012
comment
Я не уверен, что это специфично для Java, но мне нужно было перевернуть endianess для активного каталога guid/nativeGuid, и использование этого кода приводит к ошибке. Исправление в последних двух заменах байтов, которых нет в коде: java[6]=net[7]; Java[7]=сеть[6]; Также обратите внимание, что эти два метода идентичны, и вы можете сократить их до одной функции переворота. - person Maverik; 18.06.2013
comment
Так вы говорите, что вместо net[6] = java[6] и net[7] = java[7] нужно поменять местами эти два байта? Я знаю, что этот код работал для меня, обрабатывая Guids в MySQL (сейчас у меня нет под рукой этой БД), но ответ @Russell Troywest ниже может быть более безопасным вариантом, в конце концов. - person Paul Smith; 18.06.2013
comment
так работает Active Directory. Странная штука! Я в основном переключаюсь между DirectoryEntry.Guid и DirectoryEntry.NativeGuid, используя ваш код с этой модификацией. Согласно документации Guids, этот последний байт должен меняться местами в соответствии с моим базовым пониманием этого текста. - person Maverik; 18.06.2013
comment
Кажется, в вашем коде net[6] = java[6]; есть опечатка (или ошибка), должно ли это быть net[6] = java[7];? - person Felix; 07.12.2016
comment
Мне пришлось сместить net[7] на 6 и net[6] на 7 в ToBigEndian - person Alexander; 30.08.2017
comment
@ Александр, если это сработает, то не следует ли ToLittleEndian поменять местами java[6] и java[7] аналогичным образом? Предполагая это, я поменяю местами байты в обоих. Пока я не буду объединять методы в метод SwapEndianness, но, возможно, сделаю это позже. - person Paul Smith; 31.08.2017

Как уже отмечалось, двоичное кодирование 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());
}
person ᄂ ᄀ    schedule 20.02.2015

GUID.toByteArray довольно странный в C#. Первая половина имеет прямой порядок байтов, а вторая половина — прямой порядок байтов.

В комментарии на этой странице отмечается этот факт: http://msdn.microsoft.com/en-us/library/system.guid.tobytearray.aspx

порядок байтов в возвращаемом массиве байтов отличается от строкового представления значения Guid. Порядок начальной четырехбайтовой группы и следующих двух двухбайтовых групп обратный, тогда как порядок последней двухбайтовой группы и закрывающей шестибайтовой группы одинаков.

person Joey    schedule 03.12.2011
comment
сумасшедший порядок следования байтов, как заметил @Marc Gravell - person Lu55; 12.03.2015

Я думаю, ваша проблема здесь в том, что .NET имеет обратный порядок байтов, а JAVA - обратный порядок байтов, поэтому, когда вы читаете 128-битное целое число (GUID), написанное приложением C # из приложения JAVA, вам нужно выполнить преобразование из прямого порядка байтов в прямой.

person Doliveras    schedule 21.04.2011
comment
Я думаю, что порядок следования байтов C# технически определяется средой выполнения и, как оказалось, имеет порядок следования байтов в большинстве реализаций CLR. Чтобы быть действительно тщательным, всегда есть IsLittleEndian и тому подобное. Но, вероятно, отсюда и проблема. По крайней мере, это мое предположение. - person Doug Stephen; 21.04.2011

Этот код работает для меня.

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)
person iron9light    schedule 11.02.2015

Кодеки 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

person fabiolimace    schedule 23.09.2019