Example of converting String for use with ArduinoJson?

I have dug and dug and looked everywhere … and I’m sure there HAS to be a clear example of how to properly convert a String to use with ArduinoJson … but I just can not find one.

EVERYONE seems to want to just say “Don’t use Strings!” … but we HAVE TO as we already have a ton of code that can not be rewritten … and we have the spare memory. So we THOUGHT we could simply convert the String to a char array using the standard functions … but it simply refuses to work … and we know we are doing something wrong.

  String responseBodyString = "{"
      "\"status\":\"success\","
      "\"version\":\"1.0\","
      "\"timeEpoch\":1480823123,"
      "\"errorURL\":\"http://ws.website.net/errors\","
      "\"actions\": ["
        "{"
          "\"action\":\"updateProperty\","
          "\"property\":\"some.property.1\","
          "\"value\":\"some value 1\""
        "},"
        "{"
          "\"action\":\"updateProperty\","
          "\"property\":\"some.property.2\","
          "\"value\":\"some value 2\""
        "},"
        "{"
          "\"action\":\"updateProperty\","
          "\"property\":\"some.property.3\","
          "\"value\":\"some value 3\""
        "}"
      "]"
    "}";

  char responseBody[] = "{"
      "\"status\":\"success\","
      "\"version\":\"1.0\","
      "\"timeEpoch\":1480823123,"
      "\"errorURL\":\"http://ws.website.net/errors\","
      "\"actions\": ["
        "{"
          "\"action\":\"updateProperty\","
          "\"property\":\"some.property.1\","
          "\"value\":\"some value 1\""
        "},"
        "{"
          "\"action\":\"updateProperty\","
          "\"property\":\"some.property.2\","
          "\"value\":\"some value 2\""
        "},"
        "{"
          "\"action\":\"updateProperty\","
          "\"property\":\"some.property.3\","
          "\"value\":\"some value 3\""
        "}"
      "]"
    "}";

  int lengthString = responseBodyString.length();
  Serial.print(F("JSON String response length: "));  // always one char shorter than the char[]
  Serial.println(lengthString);

  int length = sizeof(responseBody);
  Serial.print(F("JSON response length: "));  // always one char longer than the string?
  Serial.println(length);
  
  // Allocate a buffer to store contents of the file.
  // std::unique_ptr<char[]> buf(new char[length]);
  
  // We don't use String here because ArduinoJson library requires the input
  // buffer to be mutable. If you don't use ArduinoJson, you may as well
  // use configFile.readString instead.
  //  configFile.readBytes(buf.get(), size);

  // attempt to convert the String to char[]
  char  buf[lengthString + 1];
  responseBodyString.toCharArray(buf, lengthString);
  buf[lengthString] = 0;  // zero terminate?  This was a guess ...
  
  StaticJsonBuffer<500> jsonStringBuffer;
  JsonObject& jsonString = jsonStringBuffer.parseObject(responseBodyString);

  if (!jsonString.success()) {
    Serial.println(F("Failed to parse responseBodyString"));  // ALWAYS FAILS!   ;-(
    return false;
  }

  StaticJsonBuffer<500> jsonBuffer;
  JsonObject& json = jsonBuffer.parseObject(responseBody);

  if (!json.success()) {
    Serial.println(F("Failed to parse responseBody"));  // ALWAYS WORKS!  ;-(
    return false;
  }

I know there must be something simple we are missing, and not understanding about doing this conversion. We don’t see any other error, except that the parse fails.

Thank you in advance … this is driving us nuts!

Ok … there are TWO bugs in the code above. One is a stupid error during debugging, and the other is related to the buffer size.

In this code snippet - updated from the code above - the attempt to parseObject(responseBodyString); should have been to parseObject(buf); … the stupid error.

  char  buf[lengthString + 1];
  responseBodyString.toCharArray(buf, lengthString + 1);  // THIS had to be enlarged to == buf size
  
  int bufLength = sizeof(buf);
  Serial.print(F("JSON buf response length: "));
  Serial.println(bufLength);

  StaticJsonBuffer<500> jsonStringBuffer;
  JsonObject& jsonString = jsonStringBuffer.parseObject(buf);

What fixed the problem was changing the parameter being passed to the .toCharArray() to be .toCharArray(buf, lengthString + 1) What we seemed to miss is that the buffer has to be one char larger than the String length, and the .toCharArray() ALSO requires this enlarged buffer size.

  int length = sizeof(responseBody);The size includes the terminating zero of the C style string.

we already have a ton of code that can not be rewritten

That is complete bullshit. You CAN rewrite that code. That you don't want to is your problem.

sizeof() is not the same as strlen(). Learn to use the proper functions at the proper time.

we have the spare memory.

HA HA HA Ha Ha ha ha ha… ooo boy. :’-D Good one!

Thank you in advance … this is driving us nuts!

Wait 'til it starts failing at random places, at random times. Now THAT’S fun!

Rewriting is probably a good exercise for you anyway. You will probably find other bugs as you work through it. It goes pretty quick, once you have a few patterns for using C strings instead of the String class. If you’re not sure how to do something, just post a question and you’ll get 6 different ways to do it.

Here’s a sketch that seems to parse ok:

#include <ArduinoJson.h>

char responseBody[] = R"raw(
{
  "status":"success",
  "version":"1.0",
  "timeEpoch":1480823123,
  "errorURL":"http://ws.website.net/errors",
  "actions": [
    {
      "action":"updateProperty",
      "property":"some.property.1",
      "value":"some value 1"
    },
    {
      "action":"updateProperty",
      "property":"some.property.2",
      "value":"some value 2"
    },
    {
      "action":"updateProperty",
      "property":"some.property.3",
      "value":"some value 3"
    }
  ]
})raw";

void setup()
{
  Serial.begin( 9600 );
  Serial.print( F("Response:") );
  Serial.print( responseBody );
  Serial.println( ':' );
  
  // Change this to a 1 to show the impact of using String.
  #if 0
    String responseBodyString( responseBody );


    int lengthString = responseBodyString.length();
    Serial.print( F("JSON String response length: ") );  // always one char shorter than the char[]
    Serial.println(lengthString);
  #endif

  int length = strlen( responseBody); // sizeof(responseBody);
  Serial.print( F("JSON response length: ") );  // always one char longer than the string?
  Serial.println(length);

  // Change this to a 0 to focus on the response memory usage, not JSON.
  #if 1
    StaticJsonBuffer<500> jsonBuffer;
    JsonObject& json = jsonBuffer.parseObject(responseBody);

    if (!json.success()) {
      Serial.println(F("Failed to parse responseBody"));
    }
  #endif
}

void loop()
{}

Notice that it uses the “raw string” technique for the responseBody. The leading ‘R’, plus a quote, plus an delimiting string (i.e., “raw” in this case), and an open paren starts the raw data. Everything up to the terminating raw)"; is part of the responseBody, even the newlines. The delimiting string could be empty if no close paren+double quote is in the raw data.

It does not work with const char nor PROGMEM, for reasons detailed on the ArduinoJson library page.

Cheers,
/dev