2014-10-17 51 views
2

因此protobuf網使用這個原內容爲.NET GUID:與Java UUID和C#的Guid protobuf網互操作

message Guid { 
    optional fixed64 lo = 1; // the first 8 bytes of the guid 
    optional fixed64 hi = 2; // the second 8 bytes of the guid 
} 

...當我編譯原爲Java類,並創建該Java UUID實例:

UUID uid = UUID.fromString("2ac9f438-f555-40b0-8880-02226b81285c"); 

...那麼它不會不管我使用uid.getMostSignificantBits()或uid.getLeastSignificantBits()作爲參數傳遞給Guid.setHi()(或Guid.setLo()) 。

無論我選擇哪種組合,在C#反序列化包含Java生成的GUID的類型,我測試的GUID值,我得到什麼似乎是字節順序問題:

expected:<2ac9f438-f555-40b0-8880-02226b81285c> 
but was:<f55540b0-f438-2ac9-5c28-816b22028088> 

。 ..或:

expected:<2ac9f438-f555-40b0-8880-02226b81285c> 
but was:<6b81285c-0222-8880-b040-55f538f4c92a> 

有沒有簡單的解決這個問題?

應該指出我是protobuf的新手,所以對這裏的可能性有點朦朧。

編輯:

對於它的價值,我也將結果提供給Guid.setLo嘗試過在一個或兩個uid.getLeast/MostSignificantBits()的結果Long.reverseBytes()/嗨,和交換這些訂單爲好,但可惜...

編輯,第二: 難怪簡單的字節順序交換上的兩個多頭不工作(從here摘錄):

開頭四字節組和後兩個兩字節 組的順序相反,而最後兩個字節組的順序和關閉六字節組的順序相同。

看到我的答案張貼到這個問題的一種可能性。如果兩種語言都要在應用程序代碼中使用它們的本地二進制guid/uuid類型,則不確定是否有其他方式。任何其他建議(除了發送guid作爲字符串)?

+2

我發現了,太晚,該GUID使用什麼,我只能形容爲「瘋狂的字節順序」 – 2014-10-17 17:36:16

+0

@MarcGravell - FWIW到目前爲止,我很享受與protobuf網工具鏈的工作超過了相應的工具爲java。對於應用程序集成來說,向後翻轉是簡單,優雅和低摩擦的,但到目前爲止我還沒有遇到過使用java protobuf工具的情況。我認爲互操作性的要求會在某些地方沿用某種語言的作品。但是,所有人都說,帽子脫離了protobuf網! – Hoobajoob 2014-10-17 19:15:42

回答

2

好的,所以對此的一種解決方法是首先定義包含Guid的C#類,以便Guid類型不參與序列化/反序列化,而是使用字節數組。這可能看起來像這樣:

[ProtoContract] 
public class Entity 
{ 
    private bool _idInitialized = false; 
    private Guid _id; 

    public Guid id 
    { 
     get 
     { 
      if (!_idInitialized) 
      { 
       _id = new Guid(idBytes); 
       _idInitialized = true; 
      } 

      return _id; 
     } 
    } 

    [ProtoMember(1, IsRequired = true)] 
    public byte[] idBytes; 

    [ProtoMember(2, IsRequired = true)] 
    public String name; 

    // For application code, sending side 
    public Entity(Guid theId, String theName) 
    { 
     if (String.IsNullOrEmpty(theName)) 
     { 
      throw new ArgumentNullException("theName"); 
     } 

     idBytes = theId.ToByteArray(); 
     _id = theId; 
     _idInitialized = true; 
     name = theName; 
    } 

    // For protobuf-net, receiving side 
    public Entity() { } 
} 

使用之後產生該類型的原型文件GetProto:

Serializer.GetProto<Entity>() 

將會產生這樣的:

message Entity { 
    required bytes idBytes = 1; 
    required string name = 2; 
} 

使用protoc編譯該Java類。我將一個使用生成的protobuf類的包裝類放在一起。這個包裝類使用下面的轉換方法來代表包裝類的進出方式重新排序字節。 ByteBuffer和Apache Commons的ArrayUtils.reverse()完成了所有的工作。

如果需要與其他語言進行互操作,可以使用類似的方法。每種語言都可以通過該語言的一些等效實用程序符合.NET字節排序方案。

import org.apache.commons.lang3.ArrayUtils; 

public class Utilities { 

    public static UUID getUuidFromDotNetGuidBytes(byte[] guidBytes) { 
     ByteBuffer bb = ByteBuffer.wrap(guidBytes); 

     byte[] first4 = new byte[4]; 
     bb.get(first4); 
     ArrayUtils.reverse(first4); 

     byte[] second2 = new byte[2]; 
     bb.get(second2); 
     ArrayUtils.reverse(second2); 

     byte[] third2 = new byte[2]; 
     bb.get(third2); 
     ArrayUtils.reverse(third2); 

     long lsb = bb.getLong(); 

     bb = ByteBuffer.wrap(new byte[8]); 
     bb.put(first4); 
     bb.put(second2); 
     bb.put(third2); 

     bb.rewind(); 
     long msb = bb.getLong(); 

     return new UUID(msb, lsb); 
    } 

    public static byte[] getDotNetGuidBytes(UUID theUuid) { 

     ByteBuffer first8 = ByteBuffer.allocate(8); 
     first8.putLong(theUuid.getMostSignificantBits()); 
     first8.rewind(); 

     byte[] first4 = new byte[4]; 
     first8.get(first4); 
     ArrayUtils.reverse(first4); 

     byte[] second2 = new byte[2]; 
     first8.get(second2); 
     ArrayUtils.reverse(second2); 

     byte[] third2 = new byte[2]; 
     first8.get(third2); 
     ArrayUtils.reverse(third2); 

     ByteBuffer converted16 = ByteBuffer.allocate(16); 
     converted16.put(first4); 
     converted16.put(second2); 
     converted16.put(third2); 

     ByteBuffer last8 = ByteBuffer.allocate(8); 
     last8.putLong(theUuid.getLeastSignificantBits());   
     last8.rewind(); 

     converted16.put(last8); 

     return converted16.array(); 
    } 

}