Search code examples
jsondelphidelphi-xe2

How to parse JSON with XE2 dbxJSON


I am using XE2 and trying to parse a JSON file sent by a robot to a URL. I've looked at several solutions, but most don't apply due to my version of Delphi. I tried to apply the solution I found here: How to parse nested JSON object in Delphi XE2?, but I can't seem to figure out how to get the information I need.

JSON string:

{"ATTITUDE":{"message_information":{"counter":48023,"frequency":10.002707481384277,"time":{"first_message":"2021-06-02T03:17:20.370144292+00:00","last_message":"2021-06-02T04:37:22.120250362+00:00"}},"pitch":0.5178558826446533,"pitchspeed":0.04289548471570015,"roll":-3.072502613067627,"rollspeed":0.08245258033275604,"time_boot_ms":4827404,"type":"ATTITUDE","yaw":1.652945876121521,"yawspeed":0.1658300906419754},"BATTERY_STATUS":{"battery_function":{"type":"MAV_BATTERY_FUNCTION_UNKNOWN"},"battery_remaining":99,"charge_state":{"type":"MAV_BATTERY_CHARGE_STATE_UNDEFINED"},"current_battery":4,"current_consumed":52,"energy_consumed":-1,"id":0,"mavtype":{"type":"MAV_BATTERY_TYPE_UNKNOWN"},"message_information":{"counter":48022,"frequency":10.0024995803833,"time":{"first_message":"2021-06-02T03:17:20.384144633+00:00","last_message":"2021-06-02T04:37:22.032157658+00:00"}},"temperature":32767,"time_remaining":0,"type":"BATTERY_STATUS","voltages":[65535,65535,65535,65535,65535,65535,65535,65535,65535,65535]},"GLOBAL_POSITION_INT":{"alt":0,"hdg":9470,"lat":0,"lon":0,"message_information":{"counter":48023,"frequency":10.002707481384277,"time":{"first_message":"2021-06-02T03:17:20.364293452+00:00","last_message":"2021-06-02T04:37:22.114859879+00:00"}},"relative_alt":0,"time_boot_ms":4827404,"type":"GLOBAL_POSITION_INT","vx":6,"vy":-12,"vz":0},"GPS_RAW_INT":{"alt":0,"alt_ellipsoid":0,"cog":0,"eph":9999,"epv":0,"fix_type":{"type":"GPS_FIX_TYPE_NO_GPS"},"h_acc":0,"hdg_acc":0,"lat":0,"lon":0,"message_information":{"counter":4773,"frequency":0.9943749904632568,"time":{"first_message":"2021-06-02T03:17:20.950291643+00:00","last_message":"2021-06-02T04:37:21.690087398+00:00"}},"satellites_visible":0,"time_usec":0,"type":"GPS_RAW_INT","v_acc":0,"vel":0,"vel_acc":0},"HEARTBEAT":{"autopilot":{"type":"MAV_AUTOPILOT_ARDUPILOTMEGA"},"base_mode":{"bits":81},"custom_mode":19,"mavlink_version":3,"mavtype":{"type":"MAV_TYPE_SUBMARINE"},"message_information":{"counter":4803,"frequency":1.0002082586288452,"time":{"first_message":"2021-06-02T03:17:18.825804090+00:00","last_message":"2021-06-02T04:37:21.134719064+00:00"}},"system_status":{"type":"MAV_STATE_CRITICAL"},"type":"HEARTBEAT"},"MISSION_CURRENT":{"message_information":{"counter":48023,"frequency":10.002707481384277,"time":{"first_message":"2021-06-02T03:17:20.346736191+00:00","last_message":"2021-06-02T04:37:22.092055518+00:00"}},"seq":0,"type":"MISSION_CURRENT"},"NAMED_VALUE_FLOAT":{"message_information":{"counter":422608,"frequency":87.98834228515625,"time":{"first_message":"2021-06-02T03:17:18.835553199+00:00","last_message":"2021-06-02T04:37:22.112692363+00:00"}},"name":["S","t","i","c","k","M","o","d","e","\u0000"],"time_boot_ms":4827404,"type":"NAMED_VALUE_FLOAT","value":0.0},"NAV_CONTROLLER_OUTPUT":{"alt_error":-3.370908737182617,"aspd_error":0.0,"message_information":{"counter":48023,"frequency":10.002707481384277,"time":{"first_message":"2021-06-02T03:17:20.346971656+00:00","last_message":"2021-06-02T04:37:22.097877400+00:00"}},"nav_bearing":94,"nav_pitch":29.67095375061035,"nav_roll":-176.0414276123047,"target_bearing":90,"type":"NAV_CONTROLLER_OUTPUT","wp_dist":0,"xtrack_error":0.0},"PARAM_VALUE":{"message_information":{"counter":690,"frequency":172.5,"time":{"first_message":"2021-06-02T03:17:17.942380315+00:00","last_message":"2021-06-02T03:17:22.780401008+00:00"}},"param_count":689,"param_id":["R","C","1","6","_","D","Z","\u0000","\u0000","\u0000","\u0000","\u0000","\u0000","\u0000","\u0000","\u0000"],"param_index":688,"param_type":{"type":"MAV_PARAM_TYPE_INT16"},"param_value":0.0,"type":"PARAM_VALUE"},"POWER_STATUS":{"Vcc":4796,"Vservo":4817,"flags":{"bits":6},"message_information":{"counter":48023,"frequency":10.002707481384277,"time":{"first_message":"2021-06-02T03:17:20.338866582+00:00","last_message":"2021-06-02T04:37:22.086196344+00:00"}},"type":"POWER_STATUS"},"RAW_IMU":{"id":0,"message_information":{"counter":48022,"frequency":10.0024995803833,"time":{"first_message":"2021-06-02T03:17:20.392561785+00:00","last_message":"2021-06-02T04:37:22.077733412+00:00"}},"temperature":0,"time_usec":532436271,"type":"RAW_IMU","xacc":577,"xgyro":82,"xmag":-221,"yacc":-13,"ygyro":42,"ymag":319,"zacc":836,"zgyro":165,"zmag":-254},"RC_CHANNELS":{"chan10_raw":1100,"chan11_raw":1100,"chan12_raw":0,"chan13_raw":0,"chan14_raw":0,"chan15_raw":0,"chan16_raw":0,"chan17_raw":0,"chan18_raw":0,"chan1_raw":1500,"chan2_raw":1500,"chan3_raw":1500,"chan4_raw":1500,"chan5_raw":1500,"chan6_raw":1500,"chan7_raw":1500,"chan8_raw":1500,"chan9_raw":1100,"chancount":0,"message_information":{"counter":48023,"frequency":10.002707481384277,"time":{"first_message":"2021-06-02T03:17:20.368143023+00:00","last_message":"2021-06-02T04:37:22.118597838+00:00"}},"rssi":0,"time_boot_ms":4827404,"type":"RC_CHANNELS"},"SCALED_IMU2":{"message_information":{"counter":48022,"frequency":10.0024995803833,"time":{"first_message":"2021-06-02T03:17:20.394756436+00:00","last_message":"2021-06-02T04:37:22.079559371+00:00"}},"temperature":0,"time_boot_ms":4827403,"type":"SCALED_IMU2","xacc":607,"xgyro":79,"xmag":0,"yacc":-197,"ygyro":45,"ymag":0,"zacc":850,"zgyro":166,"zmag":0},"SCALED_PRESSURE":{"message_information":{"counter":48022,"frequency":10.0024995803833,"time":{"first_message":"2021-06-02T03:17:20.396526198+00:00","last_message":"2021-06-02T04:37:22.081281790+00:00"}},"press_abs":985.1317138671875,"press_diff":-0.42578125,"temperature":3843,"time_boot_ms":4827403,"type":"SCALED_PRESSURE"},"SERVO_OUTPUT_RAW":{"message_information":{"counter":48023,"frequency":10.002707481384277,"time":{"first_message":"2021-06-02T03:17:20.366297689+00:00","last_message":"2021-06-02T04:37:22.116724015+00:00"}},"port":0,"servo10_raw":0,"servo11_raw":0,"servo12_raw":0,"servo13_raw":0,"servo14_raw":0,"servo15_raw":0,"servo16_raw":0,"servo1_raw":1500,"servo2_raw":1500,"servo3_raw":1500,"servo4_raw":1500,"servo5_raw":1500,"servo6_raw":1500,"servo7_raw":1100,"servo8_raw":1500,"servo9_raw":0,"time_usec":532437021,"type":"SERVO_OUTPUT_RAW"},"STATUSTEXT":{"message_information":{"counter":3,"frequency":null,"time":{"first_message":"2021-06-02T03:17:18.844799660+00:00","last_message":"2021-06-02T03:17:18.847208006+00:00"}},"severity":{"type":"MAV_SEVERITY_INFO"},"text":["P","X","4","v","2"," ","0","0","2","6","0","0","3","8"," ","3","4","4","E","5","0","1","4"," ","2","0","3","9","3","8","4","E","\u0000","\u0000","\u0000","\u0000","\u0000","\u0000","\u0000","\u0000","\u0000","\u0000","\u0000","\u0000","\u0000","\u0000","\u0000","\u0000","\u0000","\u0000"],"type":"STATUSTEXT"},"SYSTEM_TIME":{"message_information":{"counter":96045,"frequency":20.005207061767578,"time":{"first_message":"2021-06-02T03:17:20.346859002+00:00","last_message":"2021-06-02T04:37:22.095579470+00:00"}},"time_boot_ms":4827403,"time_unix_usec":1622646643891000,"type":"SYSTEM_TIME"},"SYS_STATUS":{"battery_remaining":-1,"current_battery":3,"drop_rate_comm":0,"errors_comm":0,"errors_count1":0,"errors_count2":0,"errors_count3":0,"errors_count4":0,"load":309,"message_information":{"counter":48022,"frequency":10.0024995803833,"time":{"first_message":"2021-06-02T03:17:20.398575174+00:00","last_message":"2021-06-02T04:37:22.084398874+00:00"}},"onboard_control_sensors_enabled":{"bits":2137095},"onboard_control_sensors_health":{"bits":2161671},"onboard_control_sensors_present":{"bits":2161671},"type":"SYS_STATUS","voltage_battery":28},"VFR_HUD":{"airspeed":0.0,"alt":3.369999885559082,"climb":0.0,"groundspeed":0.0,"heading":94,"message_information":{"counter":48023,"frequency":10.002707481384277,"time":{"first_message":"2021-06-02T03:17:20.376004716+00:00","last_message":"2021-06-02T04:37:22.125711470+00:00"}},"throttle":0,"type":"VFR_HUD"},"VIBRATION":{"clipping_0":0,"clipping_1":0,"clipping_2":0,"message_information":{"counter":48022,"frequency":10.0024995803833,"time":{"first_message":"2021-06-02T03:17:20.390603900+00:00","last_message":"2021-06-02T04:37:22.037444653+00:00"}},"time_usec":4827304899,"type":"VIBRATION","vibration_x":0.772567629814148,"vibration_y":1.1524462699890137,"vibration_z":0.9556428790092468}}

To make it more simple to read, here is just the "ATTITUDE" part of the JSON:

{
  "message_information": {
    "counter": 49703,
    "frequency": 10.002615928649902,
    "time": {
      "first_message": "2021-06-02T03:17:20.370144292+00:00",
      "last_message": "2021-06-02T04:40:10.111812828+00:00"
    }
  },
  "pitch": 0.5262367725372314,
  "pitchspeed": -0.0031247171573340893,
  "roll": -3.0792529582977295,
  "rollspeed": 0.09408686310052872,
  "time_boot_ms": 4995408,
  "type": "ATTITUDE",
  "yaw": 1.6096009016036987,
  "yawspeed": 0.0655876100063324
}

I am trying to get the roll value. Here is my code:

function TForm_OV_REC.ParseJSON(sJSON,sITEM,sVALUE : String) : String;
var
  LJsonObj  : TJSONObject;
  LJPair  : TJSONPair;
  LItems : TJSONValue;
  s :string;
begin
  LJsonObj := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(sJSON),0) as TJSONObject;
  try
    LItems := LJsonObj.Get(sITEM).JsonValue;
    LJPair := TJSONPair(LItems);
    ParseJSON := TJSONPair(sVALUE).JsonString.Value;
  finally
    LJsonObj.Free;
  end;
end;

procedure TForm_OV_REC.timerRESTAPITimer(Sender: TObject);
var
  MyString,sJSON : String;
  IdHTTP: TIdHTTP;
begin
  IdHTTP := TIdHTTP.Create(nil);
  IdHTTP.ConnectTimeout := 100;
  try
    MyString := ParseJSON(IdHTTP.Get('http://192.168.2.2:4777/mavlink'),'ATTITUDE','roll');
  finally
    IdHTTP.Free;
  end;
end;

I got as far as putting the JSON string into the JSON object, but I get an Access Violation when I get to this line:

ParseJSON := TJSONPair(sVALUE).JsonString.Value;

Solution

  • Well, for starters, you don't need to convert your downloaded string to an ASCII encoded TBytes. TJSONObject.ParseJSONValue() has an overload that takes a string as input.

    Second, LJsonObj.Get('ATTITUDE').JsonValue will return a TJSONObject, which you are incorrectly type-casting to a TJSONPair. But that doesn't matter, since you are then ignoring that TJSONPair and instead incorrectly type-casting the input parameter sVALUE from a string to a TJSONPair and then trying to access its children. That is why you are getting the Access Violation.

    But even if you were able to find the TJSONPair for the roll value, you are returning the string value of its JsonString (name) property, not the string value of its JsonValue (value) property.

    Try this instead:

    function TForm_OV_REC.ParseJSON(sJSON, sITEM, sVALUE : String) : String;
    var
      LJsonVal : TJSONValue;
      LJPair : TJSONPair;
    begin
      Result := '';
      LJsonVal := TJSONObject.ParseJSONValue(sJSON);
      if LJsonVal = nil then Exit;
      try
        LJPair := (LJsonVal as TJSONObject).Get(sITEM);
        if LJPair = nil then Exit;
        LJPair := (LJPair.JsonValue as TJSONObject).Get(sVALUE);
        if LJPair = nil then Exit;
        Result := LJPair.JsonValue.Value;
      finally
        LJsonVal.Free;
      end;
    end;
    
    procedure TForm_OV_REC.timerRESTAPITimer(Sender: TObject);
    var
      MyString, sJSON : String;
      IdHTTP: TIdHTTP;
    begin
      IdHTTP := TIdHTTP.Create(nil);
      try
        IdHTTP.ConnectTimeout := 100;
        sJSON := IdHTTP.Get('http://192.168.2.2:4777/mavlink');
      finally
        IdHTTP.Free;
      end;
      MyString := ParseJSON(sJSON, 'ATTITUDE', 'roll');
      ...
    end;