Protocol Buffer: From Java to Objective-C using byte[] - java

I am currently using google's protocol buffers. It works painlessly between Java and C#, however I am running into problems trying to achieve the same use with Obj-c.
The Java WS returns a byte[]. The code that uses the protocol buffer API is simple enough:
productGroup.toByteArray();
I am able to recreate the object with Skeet's C# port, using:
byte[] result = searchWebService.SearchProductsProtocolBuffer(search);
ProductProtoGroup products = ProductProtoGroup.ParseFrom(result);
However, on the obj-c side, I am struggling to work with the return value.
The NSString I receive from the same web service RPC is this:
CmYKEzgwMDAwMUFELTEzMjUyNzk5MTQySUZPT0QgJiBCRV...
I'm not quite sure what to do with this, because I don't know what it is, save it was generated from an array of bytes. I tried parsing it directly to NSData using
NSData* data = [returnValue dataUsingEncoding:NSUTF8StringEncoding];
but on [ProductProtoGroup parseFromData:data];, I get an InvalidProtocolBuffer
I've checked on the Java side what byte string/hexadecimal representations of the original byte[], and it doesn't match the string I receive from the ws.
Hexadecimal is 0-F. Perhaps each byte was converted to a char? No, that doesn't match.
Any help would be appreciated.
Dane

With a fresher mind and some helpful comments, I finally got it.
I was wondering how the web service automagically sent a byte[] in Java, then reconstructed it in C# later, and also how to store this message later.
Turns out the string,
CmYKEzgwMDAwMUFELTEzMjUyNzk5MTQySUZPT0QgJiBCRV...
was indeed what was being sent in the soap envelopes. So inbetween creation of a byte[] in Java and transmission, something was happening. Looking at the message handler in C#,
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("",
RequestNamespace="x", ResponseNamespace="x",
Use=System.Web.Services.Description.SoapBindingUse.Literal,
ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
[return: System.Xml.Serialization.XmlElementAttribute("return",
Form=System.Xml.Schema.XmlSchemaForm.Unqualified,
DataType="base64Binary", IsNullable=true)]
public byte[] searchProductProtocolBuffer([System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] string arg0)
{
object[] results = this.Invoke("searchProductProtocolBuffer", new object[] {
arg0});
return ((byte[])(results[0]));
}
So base64Binary!, which I admittedly am meeting for the first time. Poking around, Skeet says it is the safest way:
How can I safely convert a byte array into a string and back?
So, knowing the encoding, the solution becomes straight forward. Using the algorithm presented in an answer to this question: How do I do base64 encoding on iphone-sdk?, my final code becomes:
NSString* returnValue = [WebServiceUtil processStringReturnValue:value];
NSData* data = [Encoding base64DataFromString:returnValue];
ProductProtoGroup* products = [ProductProtoGroup parseFromData:data];

I know very little about Objective C, but an NSString is no byte array. Have you tried converting the NSString to char* using -[NSString UTF8String]?

Related

Equivalent Java function Base64.encode(byte, 0) in Swift

I have the following function in Java(write inside an Android app)
Bitmap bm = BitmapFactory.decodeFile(stringPath);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 100, baos); // bm is the bitmap object era quality 100
byte[] byteData = baos.toByteArray();
byte[] newB = Base64.encode(byteData,0);
It get an Image file and convert it to a byte array.
I need that to work on Swift.
I was able to convert the byteData to Int8, the equivalent of byte array in Swift.
I use the code below:
let filename = "RES4010110001"
let test_image:UIImage = UIImage(named: filename)!
let dataImage = UIImageJPEGRepresentation(test_image, 1)! as Data
var bytes: [Int8] = dataImage.map{Int8(bitPattern: $0)}
When I print this data using print(bytes) I got the same results on iOS and Android when I compare the "byteData" from Android and "bytes" from iOS
But I don't know what is the Java equivalent function below on Swift
Base64.encode(byteData,0);
How can I create an equivalent function on Swift of the function above?
For me, it appears that this function is encoding a byte array. But I have no idea of how I can encode a Int8 Array.
I said Int8 because for me, Int8 is the Swift equivalent of byte type in Java.
edit: I want to encode a Int8 array, I guess this is what the Java function (the function in the post title) is doing inside the Android app.
You can use base64EncodedData to encode your Data to get an encoded Data, rather than working with [Int8].
The Java code passes 0 (DEFAULT) as the flags. According to the docs, this means it's compliant with RFC 2045. RFC 2045 says that lines should be no more than 76 characters, so we should pass lineLength76Characters. RFC 2045 also requires CRLF as line endings, but base64EncodedData seems to insert them automatically even if we don't pass endLineWithCarriageReturn and endLineWithLineFeed.
let filename = "RES4010110001"
if let testImage = UIImage(named: filename),
let dataImage = testImage.jpegData(compressionQuality: 1) {
let encodedData = dataImage.base64EncodedData(options: [.lineLength76Characters])
} else {
// failed to get the UIImage, or the JPEG data, handle the error here...
}
I suggest that you do not work with [Int8] here. The byte[]s in the Java code are clearly not just "lists of numbers between -128 and 127". They represent data buffers, and those are represented by the Data struct in Swift. Don't worry, Both Data and [Int8] share a very similar set of protocol conformances, like MutableCollection, RangeReplaceableCollection, RandomAccessCollection etc. You can do almost everything you can do to an Int8 array, to a Data.
I was able to find an definite answer using part of the answer created by
#Sweeper and from another answer published here on Stackoverflow
let filename = "RES4010110001"
let test_image:UIImage = UIImage(named: filename)!
let dataImage = UIImageJPEGRepresentation(test_image, 1)! as Data // use apenas este que é correto, o galvez usa jpg no android
let dataImageStringBase64:String = dataImage.base64EncodedString(options: [.lineLength76Characters])
let byteArray = [UInt8](dataImageStringBase64.utf8)
The answer posted by #Sweeper just miss this line
let byteArray = [UInt8](dataImageStringBase64.utf8)
This last line of code made the code works perfectly for me.

C sharp to Java : MemoryStream and BinaryReader

I am working on a network device and catching packets from it over network as UDP. In some parts i need to parse a byte array (packetBuffer) to get session header but i couldn't. I found a part of code but unfortunatelly it is C sharp and i also couldn't convert it to java. It is like below;
MemoryStream memstream = new MemoryStream(packetbuffer);
BinaryReader binreader = new BinaryReader(memstream);
byte[] sessionheader = binreader.ReadBytes(4);
ushort ROapdu_type = correctendianshortus(binreader.ReadUInt16());
I need to find what MemoryStream and BinaryReader in C# equivalent in Java is.
I appreciate for all your helps.
you can use getData to get byte[], then extract 4 bytes from the result array and use ByteBuffer.wrap(result4Byte).getInt() to convert it into int(you may need to use order to set byte ordering to little/big endian)

C# - double-Value transmitted to Java client via byte[]

I am transmitting a double value from a C#-mqtt client to a Java-mqtt client. Mqtt requires its payload to be a byte[] so I in c# I am doing the following:
byte[] vals = BitConverter.GetBytes(sub.value); // c#-sender
and transmitting this over mqtt to a java client, which in turn
double result = ByteBuffer.wrap(vals).getDouble(); // java-receiver
However, while the original double-value is in the range of ~1 to 10, the resulting java value is in the range of 10^-311 to 10^-312.
I am not very familiar with c# at this point and can't find the problem.
Is it an offset problem? LE/BE ? I am pretty much stuck and would love if you could give me a hint.
As mentioned in the comments, try flipping the byte order on the ByteBuffer using the ByteBuffer.order(ByteOrder) method

How to represent header value and actual message in Byte Array Java?

I need to make a byte array in which I will have header values initially and my actual message will come after the header values.
My header values will have - data center which is a string, client_id which is integer, pool_id which is also integer and data_count is also an integer.
And my actual message which will come after header values is - hello world
In my case, my header length may grow so I need to initialize that as a variable so that I can increase it later on as needed.
I am little bit confuse in how to use Byte Array here. How can I represent this in a byte array in network byte order so that c++ program can decode this out properly on ubuntu 12.04 machine?
You can use Protocol Buffers to represent the messages (header and content). It will handle the transformations between languages and different platforms. Also, it is providing room for further expansion and support for multiple message versions.
For your example you can define the message format like (eg. messageModel.proto):
package common;
option java_package = "my.java.package";
option java_outer_classname = "MessageProto";
message MyMessage {
optional string dataCenter = 1 [default = DEFAULT_DC];
optional int64 clientId = 2;
optional int64 poolId = 3;
optional int64 dataCount = 4;
optional string body = 5;
}
Then using the protoc compile like:
protoc -I src/java/ --java_out=src/java/ messageModel.proto
You will generate the transport objects and the utility classes to marshal them from one endpoint to another (representing different messages even). Please check the java tutorial for more details.
To create a MyMessage from java you will be able to do something like:
MessageProto.MyMessage.Builder mb = MessageProto.MyMessage.newBuilder();
mb.setDataCenter("aDC");
mb.setClientId(12);
mb.setPoolId(14);
mb.setDataCount(2);
mb.setbody("hello world");
MessageProto.MyMessage message = mb.build();
To transform the message into a byte array, you will use: message.toByteArray()
If C++/C is your destination you will need to generate (from the same model) the C builders and objects too. And to decode the message you will do something like:
MessageProto.MyMessage message = MessageProto.MyMessage.parseFrom(buffer);
Where buffer will represent the received content.
If this is only a homework assignment then you can serialize your header and body message using
a DataOutputStream, but I would suggest investigating Protocol Buffers as well.
Try using a DataOutputStream that is targeted to a ByteArrayOutputStream. When you're done with writing the message to the DataOutputStream, you can obtain the constructed byte array from the ByteArrayOutputStream.
Like this:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeInt(client_id);
dos.writeUTF(data_center);
// etc...
byte[] message = baos.toByteArray();
Protocol Buffers are also a good option, if you want more flexibility and higher performance. It depends on what you want to get out of this application; if it needs higher performance, or whether it's a one-off throwaway app or something that you expect to grow and be maintained in the longer future. DataOutputStream and DataInputStream are simple to use and you can start right away, you need to invest a bit more of your time to learn Protocol Buffers.

getBytes then toString don't give the same result each time

I am trying to use the sample BlueToothChat of Android but there is something I don't understand :
byte[] send = message.getBytes();
Log.d("SEND_BYTE", send.toString());
mChatService.write(send);
Here, message is a String, which is then converted to bytes, I guess in order to be sent. But When I check the log, the send.toString() part is really short even if the message I type is long. Worse, if I type twice the same message, I get 2 different logs, which is really weird I find.
Here is what I get in the log for the message hello, three times in a row :
[B#413d62e0
[B#41390078
[B#413ed3d8
There must be something (maybe really simple) I didn't get, but can(t figure out what it is. Can you help me with this?
Edit :
Maybe it is useful to add the following of the code, so here is the complete code :
byte[] send = message.getBytes();
Log.d("SEND_BYTE", send.toString());
mChatService.write(send);
// Reset out string buffer to zero and clear the edit text field (buffer is used in the write function)
mOutStringBuffer.setLength(0);
mOutEditText.setText(mOutStringBuffer);
Yes, calling toString() on a byte array is a bad idea. Arrays don't override toString(), so you get the default behaviour of Object.toString().
To reverse the String.getBytes() call, you want:
Log.d("SEND_BYTE", new String(send));
Or to see the bytes more directly:
Log.d("SEND_BYTE", Arrays.toString(send));
However, I would strongly encourage you not to do that directly. Instead, you should specify an encoding when you convert to or from binary, otherwise it will use the platform default encoding. What encoding is the chat service expecting? For example, if it's expecting UTF-8:
byte[] send = message.getBytes("UTF-8");
Log.d("SEND_BYTE", Arrays.toString(send));
mChatService.write(send);
You need to create a new string object to get the actual string
String senddata=new String(send);
Try:
Log.d("SEND_BYTE", new String(send, "UTF-8"););

Categories

Resources