I want to use protobuf instead of Json to communicate between message queue.
I know how to deal with it when there is only one proto message.
Assume that the proto file is:
//person.proto
syntax = "proto3";
option java_outer_classname = "PersonProto";
message Person {
int32 id = 2;
string name = 1;
string email = 3;
}
Now, i can deal with it with the approach below:
PersonProto.Person person = PersonProto.Person.newBuilder()
.setEmail("123#test.com")
.setId(1)
.setName("name-test")
.build();
byte[] bytes = person.toByteArray();
//Transfer from publisher to consumer between message queue.
//I can deserialise it, because i know the proto message is Person.
PersonProto.Person.parseFrom(bytes);
But what if there are multiple proto messages?
Assume that there is another proto message called Address.
syntax = "proto3";
option java_outer_classname = "PersonProto";
message Person {
int32 id = 2;
string name = 1;
string email = 3;
}
message Address {
string address = 1;
}
When consumer received byte array from message queue, how to know which proto message it is? and how to deserialise the byte array?
Protobuf 3 introduced the concept of Any that can act in a similar way to the top-level-message pattern explained by #AdamCozzette.
On the write-side you pack your message in an Any:
Person person = ...
Any any = Any.pack(person);
out.write(any.toByteArray());
Then on the read-side you read into an Any and switch on the types you are interested in:
Any any = Any.parseFrom(in);
if (any.is(Person.class)
{
Person person = any.unpack(Person.class);
...
}
else if (any.is(Address.class);
{
Address address = any.unpack(Address.class);
...
}
else
{
//Handle unknown message
}
Using Any removes the need for special a message type (the top-level-message), but also removes an element of type-safety in as much as you may receive messages that the consuming code does not know how to handle.
Protocol buffers are not self-describing, so in general when you get a serialized protobuf there is no way to interpret its contents without knowing what schema to expect.
In your case I would recommend using a oneof field. You can have a single top-level message type for your queue messages and let that contain a oneof field that contains either a Person or an Address:
message TopLevelMessage {
oneof inner_message {
Person person = 1;
Address address = 2;
}
}
The consuming code would then need a switch statement like this in order to retrieve the inner-message:
TopLevelMessage topLevelMessage = TopLevelMessage.parseFrom(...);
switch (topLevelMessage.getInnerMessageCase())
{
case PERSON:
Person person = topLevelMessage.getPerson();
...
break;
case ADDRESS:
Address address = topLevelMessage.getAddress();
...
break;
default:
...
}
Related
I have a server set up to send messages over a local host port. I am trying to decode the serialized json messages sent by the server and get this error.
Error decoding message: kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 55: Expected EOF after parsing, but had instead at path: $
JSON input: .....mber":13,"Timestamp":5769784} .....
The Racer State messages are formatted in JSON as follows: { “SensorId”: “value”, “RacerBibNumber” : “value”, “Timestamp” : “value” }, where the value’s are character string representations of the field values. I have also tried changing my RacerStatus Class to take String instead of Int but to a similar error. Am I missing something here? The symbol that is missing in the error was not able to be copied over so I know it's not UTF-8.
I have also added
val inputString = bytes.toString(Charsets.UTF_8)
println("Received input: $inputString")
This gets
Received input: {"SensorId":0,"RacerBibNumber":5254,"Timestamp":3000203}
with a bunch of extraneous symbols at the end.
data class RacerStatus(
var SensorId: Int,
var RacerBibNumber: Int,
var Timestamp: Int
) {
fun encode(): ByteArray {
return Json.encodeToString(serializer(), this).toByteArray()
}
companion object {
fun decode(bytes: ByteArray): RacerStatus {
print(bytes[0])
try {
val mstream = ByteArrayInputStream(bytes)
return Json.decodeFromStream<RacerStatus>(mstream)
} catch (e: SerializationException) {
println("Error decoding message: $e")
return RacerStatus(0, 0, 0)
}
// return Json.decodeFromString(serializer(), mstream.readBytes().toString())
}
}
}
So I found an answer to my question. I added a regex to include just the json components I know my json contains.
val str = bytes.toString(Charsets.UTF_8)
val re = Regex("[^A-Za-z0-9{}:,\"\"]")
return Json.decodeFromString<RacerStatus>(re.replace(str,""))
I thought that Charsets.UTF_8 would remove the misc characters but it did not. Is there a more intiuative solution? Also is there a regex that would cover all possible values in json?
I would like to create a protobuf message which represents an array of objects.
Example
[
{
"firstKey": "firstValue",
"secondKey": "secondValue",
},
{
"firstKey": "firstValue",
"secondKey": "secondValue",
},
...
]
Pseudo code (not a valid syntax)
syntax = "proto3";
message Entry {
string firstKey = 1;
string secondKey = 2;
}
repeated message Response {
...Entry;
}
I cannot find a way to do this. Is it even possible or am I forced to nest it like this?
syntax = "proto3";
message Entry {
string firstKey = 1;
string secondKey = 2;
}
message Response {
repeated Entry data = 2;
}
repeated is only permitted on message fields (use of type) not (definition of) types:
https://developers.google.com/protocol-buffers/docs/overview#specifying_field_rules
This makes sense as your alternative would require that the type is always repeated which is less useful; if you have "many of" you're likely to want to use "one of" too.
You can nest the definition so that it is only applicable to the referencing type:
https://developers.google.com/protocol-buffers/docs/overview#nested
Sorry for my weak English. I'm trying to receive the json data from Sim800 on my Arduino. To read the data on the serial port I used the following code:
while(serialSIM800.available()==0); //Wait until the data is received
String content = "";
while(serialSIM800.available()>0){ // When data is received
content = content + char(char (serialSIM800.read()));
}
Serial.print(content);
But incomplete data is received. As follows:
{"id":"1212","temp":"24","hum","4
For the better result I used the following code:
byte x;
char data[128];
void sim800Reply() {
x=0;
do{
while(serialSIM800.available()==0);
data[x]=serialSIM800.read();
Serial.print(data[x]);
x++;
} while(!(data[x-1]=='K'&&data[x-2]=='O'));
}
Data is completely received. As follows:
{"id":"1212","temp":"24","hum","45","date":"11.2018","status":"200"}
OK
But I think this code is not good and there are problems.for example If the serialSIM800 is not available like when sim800 are not connected, The following code causes crash while(serialSIM800.available()==0); Because this is always true OR If there is an error and OK Was not received, The following code causes crash while(!(data[x-1]=='K'&&data[x-2]=='O')); Because this is always true.The maximum data length is 120 bytes, What should I do to Receive the Json data from Arduino serial? Thank you all.
for start try:
if (serialSIM800.available()) {
String content = serialSIM800.readString();
Serial.print(content);
}
in setup() add serialSIM800.setTimeut(50);
Finally, I changed the code as follows:
String dump(bool printData) {
byte while_cunt = 0;
while (serialSIM800.available() == 0){
while_cunt++;
delay(15); // It can change for a proper delay
if(while_cunt >=250){ // If the data from serial was not received past 3.75 seconds
Serial.println("return");
return ""; // Exit from dump function
}
}
String content = "";
while (serialSIM800.available() > 0) {
content += serialSIM800.readString();
}
if (printData) {
Serial.println(content);
}
return content;
}
It can return and print serial data And it works fast Because every 15 milliseconds checks that the data has been received and if it has not been received for a certain period of time(In this case 3.75 seconds), it will be out of process and will not crash.
Examples for this function:
serialSIM800.println("AT");
dump(true);
// print Received response from sim800
serialSIM800.println("AT");
String str = dump(true);
// print Received response from sim800 and Saved in variable named 'str'
serialSIM800.println("AT");
String str = dump(false);
//Save response in variable named 'str' without print
I have the following struct that I want to read from JSON and Write to JSON. I want to read the PasswordHash property (deserialize it) but skip when writing the object (serialize it).
Is it possible to tag the object such that it that it is read when deserializing but ignored when serializing? the json:"-" seems to skip the field in both operations.
type User struct {
// Must be unique
UserName string
// The set of projects to which this user has access
Projects []string
// A hash of the password for this user
// Tagged to make it not serialize in responses
PasswordHash string `json:"-"`
// Is the user an admin
IsAdmin bool
}
My deserialization code is as follows:
var user User
content = //Some Content
err := json.Unmarshal(content, &user)
and the serialization code is:
var userBytes, _ = json.Marshal(user)
var respBuffer bytes.Buffer
json.Indent(&respBuffer, userBytes, "", " ")
respBuffer.WriteTo(request.ResponseWriter)
I think you can't do that with json tags, but looks like the input user and the output user are actually different semantic objects. It's better to separate them in code too. This way it's easy to achieve what you want:
type UserInfo struct {
// Must be unique
UserName string
// The set of projects to which this user has access
Projects []string
// Is the user an admin
IsAdmin bool
}
type User struct {
UserInfo
// A hash of the password for this user
PasswordHash string
}
Your deserialization code stay the same. Serialization code is changed in one line:
var userBytes, _ = json.Marshal(user.UserInfo)
play.golang.com
You can’t do that with tags. You have to implement json.Marshaler to exclude the fields you want to exclude.
It would be a little tricky to write a MarshalJSON for the struct, because you don’t want to rewrite the whole marshaling. I recommend you have a type Password string, and write marshaler for that to return something empty as its JSON representation.
Dart is great, it's like, well, programming. Also the IDE is great.
I have encountered what appears to me to be a problem (for me) with the Dart
JSON.parse function. The problem appears to me to be that if a string
is an integer, it creates an integer in the Map. This may not be desirable
because there could be situations where string fields contain numeric.
eg. an address that contains just the street number. Or, perhaps I am
simply doing something wrong.
The line of code that exposes the possible problem is :
String sId = mAcctData['I_Id'];
I presume that there is a logical solution to this, and if so I would
appreciate being advised
httpSubmitAccountNrLoadEnd(HttpRequest httpReq) {
String sResponse = null;
if (httpReq.status != 200) {
print('On Http Submit Account Nr. There was an error : status = ${httpReq.status}');
} else {
print("On Return from Http Submit Account Nr. no error - status = ${httpReq.status}");
sResponse = httpReq.responseText;
}
print("Response From Submit Account Nr. = ${sResponse}"); // print the received raw JSON text
Map mAcctData = JSON.parse(sResponse);
print ("Json data parsed to map");
print ("Map 'S_Custname' = ${mAcctData['S_Custname']}");
String sCustName = mAcctData['S_Custname'];
print ("About to extract sId from Map data");
String sId = mAcctData['I_Id'];
print ("sId has been extracted from Map data");
String sAcctNr = mAcctData['I_AcctNr'];
String sAcctBal = mAcctData['I_AcctBal'];
The following is the console output.
The line that shows the problem that I have is:
Exception: type 'int' is not a subtype of type 'String' of 'sId'.
About to send http message
On Return from Http Submit Account Nr. no error - status = 200
Response From Submit Account Nr. = {"S_Custname":"James Bond","I_Id":1,"I_Acctnr":123456789,"I_AcctBal":63727272,"I_AvailBal":0}
Json data parsed to map
Map 'S_Custname' = James Bond
About to extract sId from Map data
Exception: type 'int' is not a subtype of type 'String' of 'sId'.
Stack Trace: #0 httpSubmitAccountNrLoadEnd (http://127.0.0.1:3030/C:/Users/Brian/dart/transact01/transact01.dart:75:31)
1 submitAccountNr.submitAccountNr. (http://127.0.0.1:3030/C:/Users/Brian/dart/transact01/transact01.dart:33:59)
Exception: type 'int' is not a subtype of type 'String' of 'sId'.
Stack Trace: #0 httpSubmitAccountNrLoadEnd (http://127.0.0.1:3030/C:/Users/Brian/dart/transact01/transact01.dart:75:31)
1 submitAccountNr.submitAccountNr. (http://127.0.0.1:3030/C:/Users/Brian/dart/transact01/transact01.dart:33:59)
Questions:
Is there a solution to this? (eg. force JSON.parse to create strings)
Is there a better way to handle this? (eg. extract data directly from the JSON string)
From what I see in your sResponse mAcctData['I_Id'] is really an int :
{
"S_Custname": "James Bond",
"I_Id": 1,
"I_Acctnr": 123456789,
"I_AcctBal": 63727272,
"I_AvailBal": 0
}
So if you want to get a String for mAcctData['I_Id'] you can use int.toString() :
String sId = mAcctData['I_Id'].toString();
Just turn your integers into strings if you want them to be like that:
{
"S_Custname": "James Bond",
"I_Id": "1",
"I_Acctnr": "123456789",
"I_AcctBal": "63727272",
"I_AvailBal": "0"
}
Now it should work fine. Alternatively, use .toString() to convert the integers into strings.