How to pass sobject list to apex from lightning?

Take a look at this post. I was able to pass the sobject and sobject array using json.encode util.I have used account sobject for the sake of testing.

Passing a sobject param was/ is causing error in Lightning: https://success.salesforce.com/issues_view?id=a1p300000008XW6AAM

component:

<aura:component controller="Saveaccesscontroller">
    <aura:attribute name="AccountItems" type="Account[]" />
    <aura:attribute name="Accountval" type="Account" default="{ 'sobjectType': 'Account',
                         'Name': 'test'}"/>

     <button type="button" onclick="{!c.SaveAccessRequest}">Click Me!</button> 
</aura:component>

controller: I have queried the account list and passed it to the getReturnValue method in the server side controller.

({
    SaveAccessRequest : function(component, event, helper) {      
        var ARLIlist = component.get("v.AccountItems");
        var accessreq = component.get("v.Accountval");
        var queryaction = component.get("c.queryRecord");
        queryaction.setCallback(this, function(response) {            
            var state = response.getState();
            if (state === "SUCCESS") {
                component.set("v.AccountItems",response.getReturnValue());
                var action = component.get("c.getSaveRecord");
                action.setParams({"AR":$A.util.json.encode(accessreq),
                                  "ARLI":$A.util.json.encode(component.get("v.AccountItems"))});
                $A.enqueueAction(action);
             }
        });    
        $A.enqueueAction(queryaction);              

        }
})

Apex controller:

Define the param as string and use the convertJSONToListOfSObject method to from the post above to convert the string to list

 public class Saveaccesscontroller {
    @AuraEnabled
    public static void getSaveRecord(String AR,String ARLI){
        system.debug('====>'+ARLI);
         List<SObject> newSObjectsList = convertJSONToListOfSObject(ARLI);
        system.debug('====>'+newSObjectsList);
        system.debug('====>'+AR);
    }
    @AuraEnabled
    public static List<account> queryRecord(){
        return [select id,name from Account limit 5];
    }

     private static List<SObject> convertJSONToListOfSObject(String json) {
        object[] values = (object[])System.JSON.deserializeUntyped(json);

        List<SObject> newSObjectsList = new List<SObject>();
        for (Object v : values) {
            Map<String, Object> m = (Map<String, Object>)v;
            Schema.SObjectType targetType = Schema.getGlobalDescribe().get((String)m.get('sobjectType'));

            SObject o = targetType.newSObject();

            Map<String, Schema.SObjectField> fields = targetType.getDescribe().fields.getMap();
            for (String fieldName : m.keySet()) {
                // Filter out any psuedo fields such as LastNameLocal
                Schema.SObjectField fi = fields.get(fieldName);
                if (fi != null) {
                    if (fi.getDescribe().isCreateable() && fi.getDescribe().isUpdateable()) {
                        o.put(fieldName, m.get(fieldName)); 
                    }
                }
            }

            newSObjectsList.add(o);
        }

        return newSObjectsList;
    }
}

Debug logs:

09:38:31.3 (4142574)|USER_DEBUG|[4]|DEBUG|====>[{"Id":"001d000001xDnrmAAC","Name":"Smith Enterprises"},{"Id":"001d0000026DD5JAAW","Name":"test"},{"Id":"001d000001StO98AAF","Name":"abc"},{"Id":"001d000001StOT1AAN","Name":"abc"},{"Id":"001d000000ySkIvAAK","Name":"TEST"}] 09:38:31.3 (4172328)|STATEMENT_EXECUTE|[5] 09:38:31.3 (4180526)|HEAP_ALLOCATE|[5]|Bytes:44 09:38:31.3 (4189407)|ENTERING_MANAGED_PKG| 09:38:31.3 (4198943)|USER_DEBUG|[5]|DEBUG|====>{"sobjectType":"Account","Name":"test"}


Component:

    <aura:component controller="Saveaccesscontroller">
    <aura:attribute name="AccountItems" type="Account[]" default="{'name' : 'karthik'}" />
    <aura:attribute name="Accountval" type="Account" default="{ 'sobjectType': 'Account',
                                                              'Name': 'test'}"/>
    <ui:button label="ClickMe" press="{!c.SaveAccessRequest}"/>
</aura:component>

controller:

I have queried the account list and passed it to the getReturnValue method in the server side controller.

    ({
    SaveAccessRequest : function(component, event, helper) {      
        var ARLIlist = component.get("v.AccountItems");
        var accessreq = component.get("v.Accountval");
        var queryaction = component.get("c.queryRecord");
        queryaction.setCallback(this, function(response) {            
            var state = response.getState();
            if (state === "SUCCESS") {
                component.set("v.AccountItems",response.getReturnValue());
                var ARLIlist = component.get("v.AccountItems");
                var action = component.get("c.getSaveRecord");
                action.setParams({"AR":accessreq,
                                  "ARLI":component.get("v.AccountItems")});
                action.setCallback(this, function(response) {
                    var state = response.getState();
                }) 
                $A.enqueueAction(action);
            }
        });    
        $A.enqueueAction(queryaction);              

    }
})

Apex code:

Here i pass sObject List into Apex

    public class Saveaccesscontroller {

    @AuraEnabled
    public static void getSaveRecord(Account AR,List<Account> ARLI){
        system.debug('====>'+AR);
        Insert AR;
        system.debug('AccountList====>'+ARLI); 

        for(Integer i=0; i<ARLI.size(); i++){
            ARLI[i].Name = AR.Name;
        }
        update ARLI;
        system.debug('====>'+ARLI[0].Name);
    }

    @AuraEnabled
    public static List<account> queryRecord(){
        return [select id,name from Account limit 5];
    }

}

I did this few days back. Simply just by stringifying & deserializing the list.

Pass your params as string using JSON.stringify function.

SaveAccessRequest : function(component, event, helper) {
    var ARLIlist = component.get("v.AccessRequestLineItems");
    var action = component.get("c.getSaveRecord");
    var accessreq = component.get("v.AccessRequest");
var ARLIlistAsString = JSON.stringify(ARLIlist);
var accessreqAsString = JSON.stringify(accessreq);
    action.setParams({"ARAsString":accessreqAsString,
                     "ARLIAsString":ARLIlistAsString});
    $A.enqueueAction(action);
}

Update your apex method like this: Take the inputs as string and use JSON.deserialize to convert it back to Object or List.

@AuraEnabled
public static void getSaveRecord(String ARAsString, String ARLIAsString){

Access_Request__c AR = JSON.deserialize(ARAsString, Access_Request__c.class);
List<Access_Request_Line_Item__c> ARLI = JSON.deserialize(ARLIAsString, List<Access_Request_Line_Item__c>.class);
    Insert AR;
    system.debug('====>'+ARLI);
    for(Integer i=0; i<ARLI.size(); i++){
        ARLI[i].Access_Request_ID__c = AR.id;
    }
    Insert ARLI;
    system.debug('====>'+ARLI[0].id);

}