I've used ashxes for this problem too. To be honest I didn't know webservices had that ResponseFormat attribute. That said I think I still prefer the ashx route for lightness and control.
There's some peripheral details left out here, to concentrate on the bit you'd need.
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Namespace Handlers.Reports
Public MustInherit Class Base
Implements IHttpHandler, SessionState.IRequiresSessionState
Protected data As String()() = Nothing
Private Shared ReadOnly JsonContentType As String = "application/json"
Public Sub ProcessRequest(ByVal context As System.Web.HttpContext) Implements System.Web.IHttpHandler.ProcessRequest
Try
Me.GetData()
Me.BuildJsonResponse(context)
Catch ex As Exception
End Try
context.Response.End()
End Sub
Private Sub BuildJsonResponse(ByVal context As System.Web.HttpContext)
context.Response.AddHeader("Content-type", Base.JsonContentType)
Dim json = Me.BuildJson()
context.Response.Write(json)
End Sub
Private Function BuildJson() As String
If Not Me.data Is Nothing Then
Return String.Format("{{data: {0}, pageInfo: {{totalRowNum:{1}}}, recordType: 'array'}}", JsonConvert.SerializeObject(Me.data), Me.totalRows)
End If
Return String.Empty
End Function
End Class
End Namespace