function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Milan HrdlickaMilan Hrdlicka 

Parsing JSON String getting "System.JSONException: Unexpected character ('<' (code 60)): expected a valid value (number, String, array, object, 'true', 'false' or 'null') at input location [1,2]" error

Hi all,

In the developer console I am trying to run below code in order to get geolocation data parsed from JSON string:

getGPS gps = new getGPS();
gps.getGPSdata();
gps.getLng();
gps.getLat();

Execution of "gps.getGPSdata();" returns "System.JSONException: Unexpected character  ('<' (code 60)): expected a valid value (number, String, array, object, 'true', 'false' or 'null') at input location [1,2]" error.
Therefore "gps.getLng();" and "gps.getLat();" don't return anything.
Please see below classes - can anyone advise on what is causing this error as I guess this have worked before.
Appreciate your help.

//class no.1
--------------------

public class getGPS {
    
    String endPointString = 'https://maps.googleapis.com/maps/api/geocode/json?address=Žerotínova 1664/57, Praha, 130 00&key=AIzaSyDpkHWwId9J1mMCqu9mirXPEwpM3XTs0GU';
    double lng;
    double lat;
    
    public double getLng() {
        return lng;
        }
    
    public double getLat() {
        return lat;
        }
    
  

      public void getGPSdata() {
          Http httpProtocol = new Http();
          HttpRequest request = new HttpRequest();
          String endpoint = endPointString;
          request.setEndPoint(endpoint);
          request.setMethod('GET');
          HttpResponse response = httpProtocol.send(request); 
          String jsonString = response.getBody();
          googleAddress addr = googleAddress.parse(jsonString);
          
googleAddress.Location loc = addr.firstLoc;
double lng = loc.lng;
double lat = loc.lat;
          
          this.lng=lng;
          this.lat=lat;

      }
    
                       
}

//class no.2
--------------------


public class googleAddress {

    public class Address_components {
        public String long_name;
        public String short_name;
        public List<String> types;
    }

    public class Geometry {
        public Location location;
        public String location_type;
        public Viewport viewport;
    }

    public List<Results> results;
    public String status;
    public Location firstLoc;

    public class Results {
        public List<Address_components> address_components;
        public String formatted_address;
        public Geometry geometry;
        public Boolean partial_match;
        public String place_id;
        public List<String> types;
    }

    public class Viewport {
        public Location northeast;
        public Location southwest;
    }

    public class Location {
        public Double lat;
        public Double lng;
    }

    
    public static googleAddress parse(String json) {
        
        googleAddress returnAddr;
        
        returnAddr = (googleAddress) System.JSON.deserialize(json, googleAddress.class);
        if (!returnAddr.results.isEmpty()){
            returnAddr.firstLoc = returnAddr.results[0].geometry.location;
        }
        return returnAddr; 
        
        
    }
}



 
Best Answer chosen by Milan Hrdlicka
LBKLBK
Callouts do not work inside triggers.

You need to may need to use a Asynchronous method (@future annotation) which takes care of UPDATE part.

Here are the steps.

1. Write a @future method in a Global Class for doing the updates.
    This method has to call getGPS method and do the updates on Branch__c
    It has to take a list of Branch__c records and UPDATE them all at once.

2. Make your trigger as After Insert, After Update, so that the Async call will not fail.

I will let you give it a shot, before helping you with code.

Remember one important thing, unless you have a Premium licence for using Google Map API, you have a daily limit on how many times you can hit the endPoint to fetch the coordinates.

So, bulk processing may not be a good idea here.

All Answers

LBKLBK
You need to Encode the Address part of the endPointString variable, because you have special characters that Callout doesn't regonize.

Here is the replacement line for you.
String endPointString = 'https://maps.googleapis.com/maps/api/geocode/json?address=' + EncodingUtil.urlEncode('Žerotínova 1664/57, Praha, 130 00','UTF-8') + '&key=AIzaSyDpkHWwId9J1mMCqu9mirXPEwpM3XTs0GU';
Let me know if this helps.
 
Milan HrdlickaMilan Hrdlicka
Gret, it has helped! Thanks! 
Milan HrdlickaMilan Hrdlicka
Just one more thing.
I am trying to use this JSON parsing in a trigger.
The purpose of the trigger is to insert latitude and longtitude data to custom object "Branch__c" once data like address, city etc. are inserted so geolocation details are inserted automatically after the trigger is fired.
The point is to gather address details from Branch__c custom object and construct a String to be put in "endPointString" variable.
I am doing this by using a formula field where I want to concatenate all necessary details. 
In order to test whether the callout works in a trigger at all I chose to make an "in between steps trigger" just to test the functionality, therefore in all newly inserted rows would be put the same longtitude and latitude details as these the "endPointString" variable would have a fixed String value for the moment.
The thing is that when I use below trigger which uses above "parsing classes" and try to insert test data, I get a different error :)
Can you please help?

The error: Line: 36, Column: 1
System.DmlException: Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, updGPStest: execution of BeforeInsert caused by: System.CalloutException: Callout from triggers are currently not supported. Class.getGPS.getGPSdata: line 23, column 1 Trigger.updGPStest: line 11, column 1: []​


Data to insert:
-----------------------------
List<Branch__c> brList = new List<Branch__c> {
    new Branch__c(Account__c = '0010Y00000EhCbzQAF', City__c='Kladno', Name__c='Kladno_BULK_TEST1', Postal_Code__c='272 01', Street__c='Vrchlického', Street_No1__c='2409', Street_No2__c='9'),
    new Branch__c(Account__c = '0010Y00000EhCbzQAF', City__c='Kladno', Name__c='Kladno_BULK_TEST2', Postal_Code__c='272 01', Street__c='Vrchlického', Street_No1__c='2409', Street_No2__c='9'),
    new Branch__c(Account__c = '0010Y00000EhCbzQAF', City__c='Kladno', Name__c='Kladno_BULK_TEST3', Postal_Code__c='272 01', Street__c='Vrchlického', Street_No1__c='2409', Street_No2__c='9')
};
// Bulk insert all contacts with one DML call

insert brList;

My current trigger:
-------------------------------


 trigger updGPStest on Branch__c (before insert, before update) {
     
    List<Branch__c> addrString = new List<Branch__c>();
    getGPS gps = new getGPS();
    
    for(Branch__c br1 : Trigger.New) {
    

        addrString = [SELECT Concatenate_Address__c FROM Branch__c WHERE Id IN :Trigger.New];
        
        gps.getGPSdata();

        
        
        br1.Location_GPS__latitude__s = gps.getLat();
        br1.Location_GPS__longitude__s = gps.getLng();
        
        
         //gps.setEndPointStr('&addrString&');
        //gps.setEndPointStr('http://maps.googleapis.com/maps/api/geocode/json?address=Černokostelecká /, Říčany, 251 01');
        //br1.Test_Column__c = br1.Concatenate_Address__c;
        

    }
}
LBKLBK
Callouts do not work inside triggers.

You need to may need to use a Asynchronous method (@future annotation) which takes care of UPDATE part.

Here are the steps.

1. Write a @future method in a Global Class for doing the updates.
    This method has to call getGPS method and do the updates on Branch__c
    It has to take a list of Branch__c records and UPDATE them all at once.

2. Make your trigger as After Insert, After Update, so that the Async call will not fail.

I will let you give it a shot, before helping you with code.

Remember one important thing, unless you have a Premium licence for using Google Map API, you have a daily limit on how many times you can hit the endPoint to fetch the coordinates.

So, bulk processing may not be a good idea here.
This was selected as the best answer
Milan HrdlickaMilan Hrdlicka
Thanks for your advice, I will give it a try. Milan