Skip to content

Instantly share code, notes, and snippets.

@sciolist
Created October 16, 2015 06:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sciolist/c18934df7c3ee75ddda2 to your computer and use it in GitHub Desktop.
Save sciolist/c18934df7c3ee75ddda2 to your computer and use it in GitHub Desktop.
Convert Viewstates to Json, for debugging.

ViewStateToJsonExporter

Hopefully you're free from Webforms and ViewStates by now.. But sometimes bad things happen to good people, and you need to find out why the viewstate of a decade old site is 5mb large. Inspecting the viewstate is all manners of not fun, so this little class could help!

Usage

To use the class, you'll need to overload the 'SavePageStateToPersistenceMedium' of a WebForms page, inside that you can call the Save-function of ViewStateToJsonExporter, and you'll get a nice big json dump!

The Json looks about like this:

{
  "ControlName": "ASP.controls_windows_imagecontroluploadiframe_aspx",
  "Size": 27,
  "SizeRecursive": 505,
  "Children": [
    {
      "ControlName": "System.Web.UI.HtmlControls.HtmlForm",
      "Size": 34,
      "SizeRecursive": 478,
      "Children": [
        {
          "ControlName": "System.Web.UI.WebControls.FileUpload",
          "Size": 33,
          "SizeRecursive": 33,
          "Children": [],
          "Data": "�\u0001\u000fd\u0016\u0002\u001e\u000fdata-buttonText\u0005\tVälj fil"
        },
        {
          "ControlName": "System.Web.UI.WebControls.DropDownList",
          "Size": 351,
          "SizeRecursive": 351,
          "Children": [],
          "Data": "�\u0001\u0010\u000f\u0016\u0006\u001e\u000eDataValueField\u0005\u0002Id\u001e\rDataTextField\u0005\u0004Name\u001e\u000b_!DataBoundgd\u0010\u0015\u0006\u0006Recept\nMaträtter\bSymboler\tEtiketter\u0007Logotyp\u0007Övriga\u0015\u0006$42fd1692-aa22-41b1-82ff-be8d5c15259a$e49920cf-bcca-4ca6-a4c5-ece9a84da6a8$0fb7e627-bad5-466d-ab89-7629feac9a53$809cc79e-5f79-4a3a-9a55-3daa232360d6$870015f9-71e6-4a73-bcfb-fd712c9b2f42$00000000-0000-0000-0000-000000000000\u0014+\u0003\u0006ggggggd"
        },
        {
          "ControlName": "System.Web.UI.WebControls.CheckBox",
          "Size": 37,
          "SizeRecursive": 37,
          "Children": [],
          "Data": "�\u0001\u0010\u000f\u0016\u0004\u001e\u0004Text\u0005\nSystembild\u001e\u0007Visiblegddd"
        },
        {
          "ControlName": "System.Web.UI.WebControls.Button",
          "Size": 23,
          "SizeRecursive": 23,
          "Children": [],
          "Data": "�\u0001\u000f\u0016\u0002\u001e\u0004Text\u0005\tLadda uppd"
        }
      ],
      "Data": "�\u0001\u0016\u0002\u001e\u0007enctype\u0005\u0013multipart/form-data"
    }
  ],
  "Data": "�\u0001\u0016\u0002\u001e\u0013ValidateRequestMode\u0002\u0001"
}

Still not a very fun format, but it beats trace.axd!

using System;
using System.IO;
using Sciolist.ViewState;
public namespace Site
{
public class UsagePage : Page
{
protected override void SavePageStateToPersistenceMedium(object viewState)
{
// uncomment to dump viewstates to c:\temp\logs\*.json
if (viewState != null) new ViewStateToJsonExporter().Save(this, viewState, string.Format(@"C:\\temp\\logs\\{0}.json", DateTime.Now.Ticks));
base.SavePageStateToPersistenceMedium(viewState);
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web.UI;
using Newtonsoft.Json;
namespace Sciolist.ViewState
{
public class ViewStateToJsonExporter
{
public void Save(Page basePage, object viewState, string path)
{
var data = Generate(basePage, viewState);
if (!string.IsNullOrEmpty(data))
{
File.WriteAllText(path, data);
}
}
public string Generate(Page basePage, object viewState)
{
// skip down to the control list
if (basePage == null) return null;
var pair = (Pair) viewState;
if (pair == null) return null;
var second = (Pair) pair.Second;
if (second == null) return null;
var third = (Pair) second.Second;
if (third == null) return null;
var data = Walk(third, basePage.ToString(), basePage);
return JsonConvert.SerializeObject(data, Formatting.Indented);
}
private ControlDesc Walk(object viewState, string name, Control control)
{
var desc = new ControlDesc {ControlName = name};
if (viewState == null) return desc;
var viewStateArray = (Pair)viewState;
var controlViewState = viewStateArray.First;
var childViewState = (ArrayList)viewStateArray.Second;
var data = SerializeData(controlViewState);
desc.Data = data;
desc.Size = data.Length;
desc.Children = new List<ControlDesc>();
if (childViewState != null)
{
for (var i = 0; i < childViewState.Count; i += 2)
{
var controlName = "(NONE)";
var index = (int)childViewState[i];
var value = childViewState[i + 1];
var found = false;
if (control.HasControls())
{
if (index < control.Controls.Count)
{
controlName = control.Controls[index].ToString();
found = true;
}
else
{
controlName = "(ERROR - OOB)";
}
}
if (found)
{
desc.Children.Add(Walk(value, controlName, control.Controls[index]));
}
else
{
desc.Children.Add(new ControlDesc { ControlName = controlName });
}
}
}
desc.SizeRecursive = desc.Children.Aggregate(desc.Size, (s, n) => n.SizeRecursive + s);
return desc;
}
private string SerializeData(object controlViewState)
{
var los = new LosFormatter();
var strb = new StringBuilder();
using (var str = new StringWriter(strb))
los.Serialize(str, controlViewState);
return Encoding.UTF8.GetString(Convert.FromBase64String(strb.ToString()));
}
private class ControlDesc
{
public string ControlName { get; set; }
public int Size { get; set; }
public int SizeRecursive { get; set; }
public IList<ControlDesc> Children { get; set; }
public string Data { get; set; }
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment