How to remove attributes before JSON is sent from Custom REST Apex?

It's a bit hacky, but you could remove the attributes by serialising and deserialising the SObjects to Maps, like this example:

Account acc = [Select Id, Name, (Select Id, Name From Contacts) From Account LIMIT 1];
Map<String,Object> accMap = (Map<String,Object>)JSON.deserializeUntyped(JSON.serialize(acc));

removeAttributes(accMap);

System.debug(JSON.serializePretty(accMap)); // et voila, no attributes

private void removeAttributes(Map<String,Object> jsonObj)  {
    for(String key : jsonObj.keySet()) {
        if(key == 'attributes') {
            jsonObj.remove(key);
        } else {
            if(jsonObj.get(key) instanceof Map<String,Object>) {
                removeAttributes((Map<String,Object>)jsonObj.get(key));
            }
            if(jsonObj.get(key) instanceof List<Object>) {
                for(Object listItem : (List<Object>)jsonObj.get(key)) {
                    if(listItem instanceof Map<String,Object>)  {
                        removeAttributes((Map<String,Object>)listItem);
                    }
                }
            }
        }
    }  
}

Probably not any more palatable that @KeithC's answer, but an alternative.


As it is custom Apex code, instead of directly returning the SObject, return a simple "bean" class that just has an Id and Name property with the values copied from the SObject:

public class Bean {
    public Id Id;
    public String Name;
    Bean(SObject sob) {
       this.Id = sob.Id;
       this.Name = (String) sob.get('Name');
    }
}

PS

Based on the posted code, it is the nested SObjects that you need to eliminate:

global class Aggregate {

    public Bean user {get; set;}
    public List<Bean> markets {get; set;}

    public Aggregate(User u, List<Market__c> ms) {
        user = new Bean(u);
        markets = new List<Bean>();
        for (Market__c m : ms) {
            markets.add(new Bean(m));
        }
    }
}