I wanted something a bit more baked into the framework than what Jarrett suggested, so here's what I did:
JsonDataContractActionResult:
public class JsonDataContractActionResult : ActionResult
{
    public JsonDataContractActionResult(Object data)
    {
        this.Data = data;
    }
    public Object Data { get; private set; }
    public override void ExecuteResult(ControllerContext context)
    {
        var serializer = new DataContractJsonSerializer(this.Data.GetType());
        String output = String.Empty;
        using (var ms = new MemoryStream())
        {
            serializer.WriteObject(ms, this.Data);
            output = Encoding.Default.GetString(ms.ToArray());
        }
        context.HttpContext.Response.ContentType = "application/json";
        context.HttpContext.Response.Write(output);
    }
}
JsonContract() method, added to my base controller class:
    public ActionResult JsonContract(Object data)
    {
        return new JsonDataContractActionResult(data);
    }
Sample Usage:
    public ActionResult Update(String id, [Bind(Exclude="Id")] Advertiser advertiser)
    {
        Int32 advertiserId;
        if (Int32.TryParse(id, out advertiserId))
        {
            // update
        }
        else
        {
            // insert
        }
        return JsonContract(advertiser);
    }