I have a JSON which contains information about Opportunity and related Opportunity Products, that needs to be created on Salesforce. I'm using Apex2JSON deserialization in order to create the wrapper class. But i'm having issues inserting the records, meaning: First, I need to insert the Opportunity records (top level). After that, I need to make soql query in order to get their SF ID. And the last step - I need to fetch Opportunity Product records to the matching Opportunity record and insert them.

Can anyone please help me understand how can I fetch between Opportunity Products and the appropiate Opportunity records ? Also, this class is suppose to be bulkified, which makes it a bit hearder.

Thanks !

1 Answer 1


You'll want to add a storage variable to keep track of the opportunity. Here's an example I wrote up real quick that demonstrates a starting point for how I'd approach this problem. It presumes that you don't know the account Id, Pricebook2 Id, or any Product2 Id values, but you do know the names and product codes. This code follows what I call the Aggregate-Query-Update pattern, which is something I'd recommend you read, as you'll use it a lot in Apex.

public class JsonToOpportunity {
    public class OpportunityWrapper {
        // We use this variable to tie the wrapper to the created record
        public transient Opportunity record;
        public String name;
        public String accountName;
        public String pricebookName;
        public String stage;
        public Date closeDate;
        public Decimal amount;
        public ProductWrapper[] products;
    public class ProductWrapper {
        public String productCode;
        public Decimal quantity;
        public Decimal unitPrice;
    public void createOpportunities(String jsonString) {
        OpportunityWrapper[] items = (OpportunityWrapper[])JSON.deserialize(jsonString, List<OpportunityWrapper>.class);
        Opportunity[] oppRecordsToInsert = new Opportunity[0];
        OpportunityLineItem[] oppLinesToInsert = new OpportunityLineItem[0];
        Map<String, Id> pricebookNames = new Map<String, Id>();
        Map<String, Id> accountNames = new Map<String, Id>();
        Set<String> productCodes = new Set<String>();
        Map<Id, Map<String, PricebookEntry>> productMap = new Map<Id, Map<String, PricebookEntry>>();

        // Aggregate the record data we need to query for
        for(OpportunityWrapper wrapper: items) {
            pricebookNames.put(wrapper.pricebookName, null);
            accountNames.put(wrapper.accountName, null);
            for(ProductWrapper productWrapper: wrapper.products) {
        // Query all of the data we need for products and accounts
        for(Pricebook2 record: [SELECT Name FROM Pricebook2 WHERE Name = :pricebookNames.keySet() AND IsActive = true]) {
            pricebookNames.put(record.Name, record.Id);
            productMap.put(record.Id, new Map<String, PricebookEntry>());
        for(PricebookEntry record: [SELECT Product2.ProductCode, Pricebook2Id FROM PricebookEntry WHERE Product2.ProductCode = :productCodes AND Pricebook2Id = :productMap.keySet() and IsActive = true]) {
            productMap.get(record.Pricebook2Id).put(record.Product2.ProductCode, record.Product2.Id);
        for(Account record: [SELECT Name FROM Account WHERE Name = :accountNames.keySet()]) {
            accountNames.put(record.Name, record.Id);

        // Create the opportunities
        for(OpportunityWrapper wrapper: items) {
                // Associate each new opportunity to the wrapper via transient member
                wrapper.record = new Opportunity(
        insert oppRecordsToInsert;
        // Create the product line items
        for(OpportunityWrapper wrapper: items) {
            for(ProductWrapper productWrapper: wrapper.products) {
                    new OpportunityLineItem(
        insert oppLinesToInsert;

There is no error handling, etc, so I wouldn't consider this code product-ready. However, it should give you an idea of how to proceed with your task.

  • Hi sfdcfox, First of all - thanks for the answer, i'll try that out ! Also, Is it possible to make it bulkified ? Meaning, insted of String jsonString -> to except List<String> jsonString ? And then using a for loop in order to deserialize ? Also, What happends if there's an error on Opportunity creation ? Will wrapper.record.Id be null ? I'm asking because i'm thinking to use partial insert
    – Omnia
    Commented Feb 13, 2021 at 13:48
  • @Omnia You can have an array of objects in JSON; this code assumes that any number of opportunities will be passed in the JSON. You could make a list of JSON strings, but this would be unusual design. And, as I stated, there's no error handling; you'd want to use Database.insert(dmlList, false) and process the errors if you want. You might also need a rollback feature in case the line items fail.
    – sfdcfox
    Commented Feb 13, 2021 at 14:58
  • That is what I thought .. Thanks a lot.
    – Omnia
    Commented Feb 13, 2021 at 16:17

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .