How to Deserialize AggregateResults?

I would have expected the serialization to work...

A workaround is to use Map<String, Object> instead (as an AggregateResult is usually consumed much like a Map) which will serialize and can be created reasonable easily:

List<Map<String, Object>> results = new List<Map<String, Object>>();
for (AggregateResult ar : [SELECT Name n FROM Account GROUP BY Name LIMIT 2]) {
    results.add(ar.getPopulatedFieldsAsMap());
}

I don't think serialization on AggregateResult works because AggregateResult is not a "real" sObject, so it doesn't have any of its own fields. JSON.deserialize ignores fields which do not map to the serialized type, but if we try with JSON.deserializeStrict, we see a different result:

List<AggregateResult> results = [select Name from Account Group by Name limit 2];
String js = JSON.serialize(results);
Object deserial = JSON.deserializeStrict(js, List<AggregateResult>.class);

This will throw "No such column 'Name' on sobject of type AggregateResult"

On the other hand, if the aggregated column is ID, this will work because AggregateResult objects have ID as a concrete field (probably inherited from sObject):

List<AggregateResult> results = [select ID from Lead Group by ID limit 2];
String js = JSON.serialize(results);
Object deserial = JSON.deserializeStrict(js, List<AggregateResult>.class);
System.debug(deserial);

This will correctly deserialize into a list of AggregateResult objects with the "ID" field populated.