Represent an array of objects as a protobuf message - 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

Related

What might cause (JSON ERROR: no value for)?

I have written some code in Kotlin that should retrieve some data for a dictionary app using the JSON Request Object. I can see that the call is made successfully. The website receiving the call shows the data being sent back but I'm not getting anything back in the results object. Logcat is showing this error (E/JSONĀ ERROR: No value for results). I'm not sure where I'm going wrong in extracting the results. Can someone point me in the right direction?
val jsonObjectRequest = JsonObjectRequest(Request.Method.GET, url, null,
{ response ->
try {
val resultsObj = response.getJSONObject("results")
val result: JSONObject = response.getJSONObject("result")
val term = result.getString("term")
val definition = result.getString("definition")
val partOfSpeech = result.getString("partOfSpeech")
val example = result.getString("example")
} catch (ex: JSONException) {
Log.e("JSON ERROR", ex.message!!)
}
},
{ error: VolleyError? -> error?.printStackTrace() })
The JSON
{
"results": {
"result": {
"term": "consistent, uniform",
"definition": "the same throughout in structure or composition",
"partofspeech": "adj",
"example": "bituminous coal is often treated as a
consistent and homogeneous product"
}
}
}
Have you checked the json format? Json Formatter
Here with this code it is valid. You had his character end line in the wrong place.
{
"results":{
"result":{
"term":"consistent, uniform",
"definition":"the same throughout in structure or composition",
"partofspeech":"adj",
"example":"bituminous coal is often treated as a consistent and homogeneous product"
}
}
}

Writing a proto file for a JSON input containing random field names

A newbie to protobuff here. I am working on compressing a JSON file using protobuff. The problem is that this JSON file comes as a response from a webserver and contains certain fields whose name are random i.e. with each request posted to the server, the key names differ. For example consider the below JSON:
{
"field1": [
{
"abc": "vala",
"def": "valb",
"ghi": "valc"
}
],
"field2": "val2",
"field3": "val3"
}
In the above json, the field names "abc", "def", "ghi" can vary each time. Is there a way in protobuf so that I get field1's value completely (like a single string or anything else) without losing the random fields inside it?
I think you want "struct.proto", i.e.
syntax = "proto3";
import "google/protobuf/struct.proto";
message Foo {
.google.protobuf.Struct field1 = 1;
string field2 = 2;
string field3 = 3;
}
or possibly (because of the array):
syntax = "proto3";
import "google/protobuf/struct.proto";
message Foo {
repeated .google.protobuf.Struct field1 = 1;
string field2 = 2;
string field3 = 3;
}
However, I should emphasize that protobuf isn't well-suited for parsing arbitrary json; for that you should use a json library, not a protobuf library.

How to know which protobuf message the byte array is?

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:
...
}

Json.Net boolean parsing issue

JObject.Parse(jsonString) is causing issue for boolean data. e.g. The json is :
{
"BoolParam": true
}
I used the following code to parse:
JObject data = JObject.Parse(str1);
foreach (var x in data)
{
string name = x.Key;
Console.Write(name + " (");
JToken value = x.Value;
Console.Write(value.Type + ")\n");
Console.WriteLine(value);
}
This print out the value as :
BoolParam (Boolean) : True
The case sensitivity causes issue as I save this json for later use. The saved json looks like
{
"BoolParam": True
}
However, when i later use it, the JObject.Parse(str) throws error as invalid Json :Unexpected character encountered while parsing value: T. Path 'BoolParam', line 2, position 15.
If I change the case from "True" to "true", it works. I dont want to add this hack to change the case when saving but is there a better way to handle this scenario.
I dont want to add this hack to change the case when saving but is
there a better way to handle this scenario.
No, you have to produce valid JSON when saving if you want to be able to later deserialize it with a JSON serializer such as Newtonsoft JSON. So fixing your saving routing is the right way to go here.
One could use anonymous types and no worry about case sensitivity of boolean type variables
public static void Main()
{
bool a = true;
JObject c = JObject.FromObject(new {BoolParam= a});
Console.WriteLine(c);
}
Output:
{
"BoolParam": true
}

Retrieving References data from JSON

I have json data in the following format
{"updates":
{"message" :"[[student:123]] is present."},
"references":[{"type":"student","full_name":"XYZ","id":123}]
}
How can I map the student name to the message using the id present over the message?
I am relatively new to JSON parsing. I am currently using EJS template to manipulate the JSON into HTML.
In that just using
<%alert(updates.message.student)%>
returns "undefined". Please help.
updates.message is a string, not a JavaScript object. You can tell by the quotes around the whole attribute. JavaScript strings don't have a student property, so you are getting undefined. You can parse out the JSON part from the string with regular expressions and then use JSON.parse() to get the JSON object. However, the student id is also in updates.references[0].id in your example.
To get the student ID, do this:
<% alert(updates.references[0].id) %>
edit: If you are really want to get the id out of the message, you need to parse it out somehow. If the message format will always be the same, you can try a regular expression or string splitting to get the part containing the id.
var id_part = json.updates.message.split(" ")[0];
//parse out just the ID number in a group
var re = /\[\[[^:]+:(\d+)\]\]/;
var matches = re.exec(id_part);
var id = matches[1];
To then get the corresponding data out of the references part, you need to loop through until you find one with the id from the message. This would work.
//Ghetto old for loop for browser compatibility
for (var i = 0; i < updates.references.length; i++) {
if (updates.references[i].id == id) {
//We have found the reference we want.
//Do stuff with that reference.
break;
}
}
try
var json = {
"updates": {
"message": "[[student:123]] is present."
},
"references": [
{
"type": "student",
"full_name": "XYZ",
"id": 123
}
]
};
alert(json.references[0].full_name);