[SalesForce] Parse nested JSON

I need to parse this JSON, and then insert the values into a parent object, and then a list of child objects into the detail page of the parent. I'm having trouble with a few issues: I am unable to populate the the parent object fields because it starts outside of the START_ARRAY token. Also, having trouble with converting certain child fields to the correct data type (Percent and dollarAmount in my JSON from String to Decimal). Lastly, I was curious how I'm able to nest my JSON Parser so that each Parent object includes their child objects that I'm also parsing. I've included the JSON, and my class below. Thanks in advance!

I have already seen the JSON2Apex site. I've tried using JSON.DeserializeUntyped, my keys are dynamic, so I've read that isn't a viable solution.

Relevant Resource I've Used: Parse JSON into Lead

This is the most relevant resource that I've used, but it doesn't included nested JSON, which is where the complexities are that I'm having trouble with:

JSON to Parse

 {"ideal":[{"product":"4","percent":"25","dollarAmount":"11250"},{"product":"5","percent":"23","dollarAmount":"10350"},{"product":"3","percent":"20","dollarAmount":"9000"},{"product":"8","percent":"18","dollarAmount":"8100"},{"product":"7","percent":"15","dollarAmount":"6750"}],"idealFiltered":[{"product":"4","percent":"25","dollarAmount":"11250"},{"product":"5","percent":"23","dollarAmount":"10350"},{"product":"3","percent":"20","dollarAmount":"9000"},{"product":"8","percent":"18","dollarAmount":"8100"},{"product":"7","percent":"15","dollarAmount":"6750"}],"InBudget":[{"product":"4","percent":"70","dollarAmount":"7000"},{"product":"8","percent":"30","dollarAmount":"3000"}]}

Class

global class MicFire {
    id thisRecId;
    Recommendation_Request__c recParams;
    public String recResponseBody { get; set; }

    Recommendation_Response__c recResponse { get; set; }

    *Rest of my HTTP Request* 

     Http http = new Http();

     HTTPResponse res = http.send(req);

     recResponseBody = res.getBody();

     System.debug(recResponseBody);


     JSONParser parser = JSON.createParser(recResponseBody);

     Map<String, sObjectField> fieldMap = new Map<String, sObjectField>{
        'product' => Recommendation_Response_Product__c.Product__c,
        'percent' => Recommendation_Response_Product__c.Amount_Recommended__c,
        'dollarAmount' => Recommendation_Response_Product__c.Amount_Recomended__c,

        'ideal' => Recommendation_Response__c.Rec_Response_Type__c,
        'Ideal Filtered' => Recommendation_Response__c.Rec_Response_Type__c,
        'inBudget' => Recommendation_Response__c.Rec_Response_Type__c
     };


       // Recommendation Response
       Recommendation_Response__c recResponse;
       List<Recommendation_Response__c> recResponseList = new Recommendation_Response__c[0];

       // Recommendation Response Products
       Recommendation_Response_Product__c recResponseProd;
       List<Recommendation_Response_Product__c> recRespProdList = new Recommendation_Response_Product__c[0];
       String fieldName;

       while(parser.nextToken() != null) {

           if(parser.getCurrentToken() == JSONToken.FIELD_NAME){
               fieldName = parser.getText();
               continue;
           }

            if(parser.getCurrentToken() == JSONToken.FIELD_NAME && (fieldName == 'ideal' || fieldName == 'Ideal Filtered' || fieldName == 'inBudget')){
                recResponse.put(fieldMap.get(fieldName), parser.getText());
                fieldName = null;
                continue;
           }

           if(parser.getCurrentToken() == JSONToken.START_ARRAY){
               recResponse = new Recommendation_Response__c();
               continue;
           }

           if(parser.getCurrentToken() == JSONToken.START_OBJECT){
               recResponseProd = new Recommendation_Response_Product__c();
               continue;
           }

           if(parser.getCurrentToken() == JSONToken.FIELD_NAME && fieldName != null && fieldMap.containsKey(fieldName)){
                recResponseProd.put(fieldMap.get(fieldName), parser.getText());
                fieldName = null;
                continue;
           }

           if(parser.getCurrentToken() == JSONToken.END_OBJECT){
               recRespProdList.add(recResponseProd);
               recResponseProd = null;
               continue;
           }

           if(parser.getCurrentToken() == JSONToken.END_ARRAY){
               recResponseList.add(recResponse);
               recResponse = null;
               continue;
           }
       }
     /*  
       for(Recommendation_Response__c recType : recResponseList){
           try{
               insert recType;
           } catch(DMLException e){
               System.debug('Error with recType Insert: ' + e.getMessage());
           }
       }

       for(Recommendation_Response_Product__c recProd : recRespProdList){
           try{
               insert recProd;
           } catch(DMLException e){
               System.debug('Error with recProd Insert ' + e.getMessage());
           }
       }
       */
       //insert recRespProdList;
       System.debug('++++++++++++++++++++++++++++++++ Recommendation Response PRODUCT List: ' + recRespProdList);
       System.debug('++++++++++++++++++++++++++++++++ Recommendation Response List: ' + recResponseList);
   }
}

Best Answer

The method JSON.deserializeUntyped works well for this type of problem. What you get returned is a map:

Map<String, Object> m = (Map<String, Object>) JSON.deserializeUntyped(jsonString);

You can use the normal map methods to get hold of the keys or to see if a key is present. So for example to access the "ideal" list items:

List<Object> ideals = (List<Object>) m.get('ideal');
if (ideals != null) {
    for (Object ideal : ideals) {
        String product = (String) ideal('product');
        String percent = (String) ideal('percent');
        String dollarAmount = (String) ideal('dollarAmount');
        ...
    }
}

Because it is known that the data is a list (array) I've just cast the object to a list. If the content is of very unpredictable type you can also use instanceof tests in your logic.

Here is your JSON having been formatter by http://jsonformatter.curiousconcept.com/; this sort of formatting makes the JSON easier to understand:

{  
  "ideal":[  
    {  
      "product":"4",
      "percent":"25",
      "dollarAmount":"11250"
    },
    {  
      "product":"5",
      "percent":"23",
      "dollarAmount":"10350"
    },
    {  
      "product":"3",
      "percent":"20",
      "dollarAmount":"9000"
    },
    {  
      "product":"8",
      "percent":"18",
      "dollarAmount":"8100"
    },
    {  
      "product":"7",
      "percent":"15",
      "dollarAmount":"6750"
    }
  ],
  "idealFiltered":[  
    {  
      "product":"4",
      "percent":"25",
      "dollarAmount":"11250"
    },
    {  
      "product":"5",
      "percent":"23",
      "dollarAmount":"10350"
    },
    {  
      "product":"3",
      "percent":"20",
      "dollarAmount":"9000"
    },
    {  
      "product":"8",
      "percent":"18",
      "dollarAmount":"8100"
    },
    {  
      "product":"7",
      "percent":"15",
      "dollarAmount":"6750"
    }
  ],
  "InBudget":[  
    {  
      "product":"4",
      "percent":"70",
      "dollarAmount":"7000"
    },
    {  
      "product":"8",
      "percent":"30",
      "dollarAmount":"3000"
    }
  ]
}