Convert JSON to byte array in C - c

I am new to JSON and jansson . I am trying to create a message in JSON using jansson library and send using UDP. Which requires in byte array. Buffer and length of message in bytes. I tried with json_object_size(). But it returns only number of elements in object.Please suggest me a way forward.
json_t *messagebody = json_object();
json_object_set_new(messagebody, "request_id", request_id);
json_object_set_new(messagebody, "process_id", json_string(process_id));
json_object_set_new(messagebody, "process_server_id", json_string(process_server_id));
json_object_set_new(messagebody, "ip_address", json_string(my_ip_address));
json_object_set_new(messagebody, "action", action);

It seems you just call char *json_dumps(const json_t *json, size_t flags).
That will give you a char * to a null terminated string representing the encoded json data. You have to free it after you are finished with it. To get the length in bytes you should simply be able to use strlen() on the result.
The flags are explained in API reference under 'encoding'.

Related

Storing binary data in static array

I'm writing an application which reads data from a UART interface. The data is sent in packets. Each packet has a channel associated with it. My application multiplexes received packets into virtual channels (threads) so that every channel can work independently of one another. When I receive a packet I have to do something depending on it's contents and produce a response. The response is sent back using the same UART interface.
The data sent is mostly binary. When I'm reading from the UART interface, I know the size of the packet beforehand, so I can preallocate memory with no problem.
The problem for me is producing a response. I know the maximum size of a packet, so I can create a static buffer when I'm constructing a response. If I we're to work with ASCII characters, instead of binary data, I could rely on NULL terminator to determine how long the data stored in the buffer is. However, I'm working with binary data, so using a NULL byte does not work. Instead, I have to keep a variable storing how many bytes of the buffer is used up already. I was thinking of using a custom data type for storing binary data:
typedef struct {
unsigned char buff[2048];
size_t buff_used;
} binary_data_t;
What would be a standart way of handling this?
Since you know the number of bytes you need to hold a packet, just use a flexible array member:
typedef struct
{
size_t bytes;
unsigned char data[];
} binary_data_t;
(Note that identifiers ending in _t are reserved by POSIX, and you really shouldn't be using them.)
Allocation and reading data (assumes you read() from a file descriptor):
binary_data_t *p = malloc( sizeof( *p ) + numDataBytes );
p->bytes = numDataBytes;
ssize_t bytes_read = read( uartFD, p->data, numDataBytes );
one way of doing it could be to store a pointer to where in your array next byte should be placed.
typedef struct {
unsigned char buff[2048];
char* pData;
} binary_data_t;
// at init
binary_data_t rspMsg;
rspMsg.pData = &rspMsg.buff[0];
// at entering data
*(rspMsg.pData) = data;
rspMsg.pData++;
// at sending data you know the length via
length = rspMsg.pData - &rspMsg.buff[0];
This is one way of solving this.
Can be done in many ways.

How to map enum types to character arrays?

I am TCP-IP communication over TCP.
I have specific packets to query and then I receive a response based on that.
My packet is basically a character array that i convert to string like this:
unsigned char array1 []={0,132,0,0,0,6,84,5,0,1,255,0};
std::string load_seqInit ( array1, array1 + sizeof array1 / sizeof array1[0] );
However, I want to store these arrays separately as enum values. So that I can just call the enumtype and it will map to my respective array.
enum packets{
LOAD,
READ,
RESET,
UNLOAD
}
So maybe when I call enum type LOAD it will point to the packet:
unsigned char array1 []={0,132,0,0,0,6,84,5,0,1,255,0};
then I can just convert the enum to string and proceed with my query and response.
If the arrays are 8 bytes in size you could do:
class enum packets : unsigned long {
LOAD = 0x1234567812345678, // the hex representation of the array
...
}
If they're not, you could go with a more naive implementation using an std::map<packets, array[]> that maps from an enum value to array.

How do I determine the length of an "array of structs" in a DBus message?

I'm using the libdbus API to send method calls and receive replies. For one such method I am expecting a reply of the format:
"a(sqns)"
Which in DBus signature notation means: an array of structs, where the contents of each struct is a string, uint16, int16, and string.
Using a DBusMessageIter (iterator), I can iterate through the array and structs. However, to simplify my parsing code, is there a way I can get the length of this array before iterating through?
Unfortunately I don't believe dbus_message_iter_get_fixed_array applies in this case because my array contains structs which by this definition appears to mean non-fixed size.
It is not immediately apparent to me from the API doc how to do this (or if it's possible).
A bit late to the party maybe but for future reference, I've had success using dbus_message_iter_get_element_count(). You can use it like so:
int count = dbus_message_iter_get_element_count(iter);
if(count > 0) {
DBusMessageIter arriter;
dbus_message_iter_recurse(iter, &arriter);
for(int i = 0; i < count; ++i) {
// read items from arriter
}
}
dbus_message_iter_next(iter);

Isn't it possible to set binary Data as cookie?

Im reading from dev/random and storing the binary data into an array.
When I write the array into my log its displayed as I want:
DebugLogMsg10 (pDebugLogger, sizeThreadID, "login failed->%s", UserSession.szToken);
But when I put it into my page build function call (the second parameter is expecting char * as pointer to the string which I want to be set as cookie.
PutMainLogIn (UsersAuthentication, UserSession.szToken, &requestFcgx);
If I do for example:
PutMainLogIn (UsersAuthentication, "testtest", &requestFcgx);
It is also working fine and a cookie with the value testtest is set.
So What is the problem?
Is it not allowed to set binary data as cookie? Or Am I missunderstanding something?
ps:
this is the structure looking like:
typedef struct Sesseion_s
{
size_t sizeID;
char szToken[TOKEN_LEN];
} Sesseion_t;
And this is the function prototype:
void PutMainLogIn (UsersAuth_t pUserData, char *szToken, FCGX_Request *Fcgx_Request)

dynamic char array sizing

In my application, I have a char array defined which can take one of three options: "okay", "high", "low" which are then sent down a serial port to a remote device. I currently have the array sized to take the 4 character words plus carriage return and line feed, but when I have to send "low" I get a null character in the strings, which I am concerned would confuse the host terminal.
array definition
char mod1_status_char[6] = {'0','0','0','0','0','0'};
char mod2_status_char[6] = {'0','0','0','0','0','0'};
char mod3_status_char[6] = {'0','0','0','0','0','0'};
sample of switch case statement:
void DCOKStatus(uint8_t *ptr_status)
{
uint8_t status = *ptr_status;
switch (status)
{
case 0x00:
strcpy(mod1_status_char, "okay");
strcpy(mod2_status_char, "okay");
strcpy(mod3_status_char, "okay");
break;
case 0x10:
strcpy(mod1_status_char, "okay");
strcpy(mod2_status_char, "okay");
strcpy(mod3_status_char, "low");
break;
}
This is the struct which makes the message string to send
strcpy(MsgStatus_on.descriptor_msg, "$psu_");
MsgStatus_on.address01 = hex_addr[0];
MsgStatus_on.address02 = hex_addr[1];
MsgStatus_on.space01 = 0x20;
strcpy(MsgStatus_on.cmdmsg01, "op_en op1_");
strcpy(MsgStatus_on.statusmsg01, mod1_status_char);
MsgStatus_on.space02 = 0x20;
strcpy(MsgStatus_on.cmdmsg02, "op2_");
strcpy(MsgStatus_on.statusmsg02, mod2_status_char);
MsgStatus_on.space03 = 0x20;
strcpy(MsgStatus_on.cmdmsg03, "op3_");
strcpy(MsgStatus_on.statusmsg03, mod3_status_char);
MsgStatus_on.CR = 0x0D;
MsgStatus_on.LF = 0x0A;
and this sends the message
void USARTWrite(char *object, uint32_t size)
{
GPIO_SetBits(GPIOB, GPIO_Pin_1);
char *byte;
for (byte = object; size--; ++byte)
{
USART_SendData(USART1,*byte);
}
Would anyone be able to suggest a good approach to dynamically size the array to one character shorter when I need to send "low"?
Thanks
I don't think a dynamically sized array is called for. There are two ways in C literally to dynamically size an array: allocate it with malloc or similar; or use C99 VLAs. But in this case where you have strings of different lengths, surely the important point is to write the correct bytes in the correct order? Personally I'd prefer something like this, maybe:
char * strings[] = {"okay\r\n", "high\r\n", "low\r\n"};
serial_send(strings[msg_number], strlen(strings[msg_number]));
You don't have to call strlen, necessarily, you could store the lengths in another array. But even on the tiniest embedded device, counting to 6 takes very little time compared with sending serial data.
Of course I'm assuming that whatever function you call to actually send the data, takes a pointer and a length. But if it doesn't, I don't see how a dynamically sized array helps either.
I think the general problem here which makes it difficult to answer the question, is that you don't really say what the "size" of your array is, or why it has anything to do with the number of bytes actually written to the serial port.
Edit: with your additional explanation, the key thing seems to be this struct that the three individual strings are "passed into". Not sure what passing a string into a struct means. If it currently looks like this:
struct serialmessage {
char first[6];
char second[6];
char third[6];
};
serialmessage msg;
memcpy(msg.first, mod1_status_char, 6); // etc.
Then maybe it would be better to do this:
char *char mod1_status_char; // etc.
switch(status) {
case 0x00:
mod1_status_char = strings[0]; // or #define STATUS_OK 0
mod2_status_char = strings[0];
mod3_status_char = strings[0];
break;
case 0x10:
mod1_status_char = strings[0];
mod2_status_char = strings[0];
mod3_status_char = strings[2]; // STATUS_LOW
};
serialmessage msg[3*MAX_STRING_LENGTH+1];
strcpy(msg, mod1_status_char); // or use stpcpy if you have it
strcat(msg, mod2_status_char);
strcat(msg, mod3_status_char);
Then send the struct using strlen(msg). msg isn't exactly "dynamic" here, but the length of the string in it varies according to the data, which might be what you want. Or maybe I'm still misunderstanding the role of these three char arrays.
Copying the strings more than necessary just seems to me to introduce complications. Refer to them by pointer until the last possible moment, when your message is assembled, and you minimise the number of places in your code where you have to get buffer sizes right.
"The status_char arrays are then passed to a struct, which is then sent using a send routine."
Be very careful doing this, depending on how you code it you can get all kinds of junk in there. Remember in C the compiler can pad structs however it pleases.
As a side note, your string buffers are too short to hold a string correctly. With 4 character + CR + LF you need a buffer of 7 characters as you need to store the null terminator '\0'. If you do not do this, do not use any 'str' functions as you are not dealing with proper C strings, all your going to do is create an issue further down the road when someone goes to read this/make a change and finds out after copying a str around your hacking off the null termination (strcopy is copying "low\0" into your buffer, your apparently tossing /r/n onto the end somewhere else for some reason) use memcpy.
Onto a solution:
Why are you copying these string around at all? Why not just send an indication to your send function to tell it what it should send and just have the string statically allocated?
You could create an enum with values for (E_LOW,E_OKAY,E_HIGH), just send in 3 enums to a send function and have it store the actual strings to send as static variables locally. If space is an issue you could use bit flags instead of an enum.
Your send function just needs to copy the string value a byte at a time into the send buffer and send strlen() bytes.
Am I missing something here? The send routine should just use strlen() on the string so it only sends the data in the buffer.
serWrite( mod1_status, strlen( mod1_status));
serWrite( "\r\n", 2);
You got few options to choose from:
Why you are sending the text if the options are predefined?
You could send just the ID.
Normally, protocol messages are not fixed length except some rare cases.
Length first, message second,
CRC third.
Fill empty char array space with spaces and trim the string on the other side.
Not an option, I did not wrote this.
By truncating "head" of array.
Suppose you have char words[5] (or 6 -- to hold "\r\n").
So in case of "okay" and "high" you send content of words starting from first element -- words, and in case of low just send content starting from second one: words + 1.
EDIT: of course in this case you should write "low" starting from words[1], not words[0].
I'd like to see your definition of MsgStatus_on.
I'm betting you have something like this:
tyepdef struct {
char descriptor_msg[6];
char address01;
char address02;
char space01;
char cmdmsg01[11];
char statusmsg01[6];
char space02;
char cmdmsg02[5];
char statusmsg02[6];
char space03;
char cmdmsg03[5];
char statusmsg03[6];
char CR;
char LF;
} MsgStatus;
MsgStatus MsgStatus_on;
And then my guess is that you're doing a straight byte pointer when you call USARTWrite, like so:
USARTWrite ((char *)&MsgStatus_on, sizeof(MsgStatus_on));
If this is the case then it's copying the extra byte in your buffer. In fact it should be putting extra \0's on all of your char arrays. Unless you declared all of them one less than I did and you're actually overrunning your arrays when you do the strcpy(). It's not causing you problems because you then set the overrun memory in the next statement.
An alternative option could be to use sprintf:
char *message[100] //or some size big enough to hold the whole message.
sprintf (message, "$psu_%d%d op_en op1_%s op2_%s op3_%s\r\n",
address01, address02, mod1_status_char, mod2_status_char, mod3_status_char);
Then call:
USARTWrite (message, strlen(message));
EDIT: Oops I guess this question is quite old. Oh well, I'll leave the answer in case it's useful to you.

Resources