|
| 1 | +--- |
| 2 | +title: Converting Filter States in RadPivotGrid with RadPersistenceManager After Security Update |
| 3 | +description: Learn how to update RadPivotGrid filter states stored by RadPersistenceManager after a recent security update in Telerik.Web.UI. |
| 4 | +type: how-to |
| 5 | +page_title: How to Update RadPivotGrid Filter States in RadPersistenceManager |
| 6 | +meta_title: How to Update RadPivotGrid Filter States in RadPersistenceManager |
| 7 | +slug: pivotgrid-update-old-persisted-filter-states-after-security-update |
| 8 | +tags: persistenceframework, ui-for-asp.net-ajax, radpivotgrid, radpersistencemanager, filters-persistence, filter-states |
| 9 | +ticketid: 1694223 |
| 10 | +res_type: kb |
| 11 | +--- |
| 12 | + |
| 13 | +## Environment |
| 14 | + |
| 15 | +| Version | Product | Author | |
| 16 | +| --- | --- | ---- | |
| 17 | +| 2024.4.1114 | PivotGrid for Telerik UI for ASP.NET AJAX | [Attila Antal](https://github.com/attilaantal)| |
| 18 | + |
| 19 | +## Description |
| 20 | + |
| 21 | +I need to update legacy filter states in `RadPivotGrid` because a security update in Telerik.Web.UI removed support for filters serialized using the unsafe .NET BinaryFormatter. Older persisted filter states fail to deserialize after the upgrade, resulting in errors. While manually removing old persisted values, reapplying filters, and saving works, I need a scalable solution for hundreds of saved views. |
| 22 | + |
| 23 | +This knowledge base article also answers the following questions: |
| 24 | + |
| 25 | +- How can I convert persisted filter states in `RadPivotGrid`? |
| 26 | +- How do I handle filter serialization issues after upgrading Telerik.Web.UI? |
| 27 | +- What is the workaround for updating legacy filter states in `RadPivotGrid`? |
| 28 | + |
| 29 | + |
| 30 | +## Solution |
| 31 | + |
| 32 | +To convert legacy filter states in RadPivotGrid to the new JSON-based format, use the following approach. This code handles scenarios where filter states are stored as strings or files. |
| 33 | + |
| 34 | +The following example shows how to convert the persisted state: |
| 35 | + |
| 36 | +````C# |
| 37 | +// The actual logic for converting the state |
| 38 | +private string ConvertState(string oldState) |
| 39 | +{ |
| 40 | + XDocument doc = XDocument.Parse(oldState); |
| 41 | + |
| 42 | + XElement filterPersistenceElement = doc.Descendants("ControlSetting") |
| 43 | + .Where(setting => setting.Element("Name").Value == "FiltersPersistence").FirstOrDefault(); |
| 44 | + |
| 45 | + if (filterPersistenceElement != null) |
| 46 | + { |
| 47 | + string[] oldFilterStates = filterPersistenceElement.Element("Value").Element("string").Value.Trim().Split(','); |
| 48 | + |
| 49 | + List<string> newFilterStates = new List<string>(); |
| 50 | + |
| 51 | + foreach (string filterState in oldFilterStates) |
| 52 | + { |
| 53 | + string[] options = filterState.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); |
| 54 | + |
| 55 | + options[2] = SerializeObject(DeserializeObject(options[2])); |
| 56 | + |
| 57 | + newFilterStates.Add(string.Join(";", options)); |
| 58 | + } |
| 59 | + |
| 60 | + filterPersistenceElement.Element("Value").Element("string").Value = string.Join(",", newFilterStates.ToArray()); |
| 61 | + } |
| 62 | + |
| 63 | + return doc.ToString(); |
| 64 | +} |
| 65 | + |
| 66 | +// Legacy code for deserializing the state |
| 67 | +// WARNING: This method uses BinaryFormatter.Deserialize(), which is deprecated and insecure. |
| 68 | +// Only use this on trusted data during migration. Do NOT use in production or with untrusted data. |
| 69 | +// See: https://learn.microsoft.com/en-us/dotnet/standard/serialization/binaryformatter-security-guide |
| 70 | +public static object DeserializeObject(string objectToDeserialize) |
| 71 | +{ |
| 72 | + byte[] unencodedArray = Convert.FromBase64String(objectToDeserialize); |
| 73 | + BinaryFormatter bf = new BinaryFormatter(); |
| 74 | + using (MemoryStream ms = new MemoryStream(unencodedArray)) |
| 75 | + { |
| 76 | + return bf.Deserialize(ms); |
| 77 | + } |
| 78 | +} |
| 79 | + |
| 80 | +// Current code for serializing the state |
| 81 | +public static string SerializeObject(object objectToSerialize) |
| 82 | +{ |
| 83 | + var jsonString = JsonConvert.SerializeObject(objectToSerialize); |
| 84 | + byte[] bytes = Encoding.UTF8.GetBytes(jsonString); |
| 85 | + return Convert.ToBase64String(bytes); |
| 86 | +} |
| 87 | +```` |
| 88 | +````VB.NET |
| 89 | +'The actual logic for converting the state |
| 90 | +Private Function ConvertState(oldState As String) As String |
| 91 | + Dim doc As XDocument = XDocument.Parse(oldState) |
| 92 | + |
| 93 | + Dim filterPersistenceElement As XElement = doc.Descendants("ControlSetting") _ |
| 94 | + .Where(Function(setting) setting.Element("Name").Value = "FiltersPersistence") _ |
| 95 | + .FirstOrDefault() |
| 96 | + |
| 97 | + If filterPersistenceElement IsNot Nothing Then |
| 98 | + Dim oldFilterStates As String() = filterPersistenceElement.Element("Value").Element("string").Value.Trim().Split(","c) |
| 99 | + |
| 100 | + Dim newFilterStates As New List(Of String)() |
| 101 | + |
| 102 | + For Each filterState As String In oldFilterStates |
| 103 | + Dim options As String() = filterState.Split(New String() {";"}, StringSplitOptions.RemoveEmptyEntries) |
| 104 | + |
| 105 | + options(2) = SerializeObject(DeserializeObject(options(2))) |
| 106 | + |
| 107 | + newFilterStates.Add(String.Join(";", options)) |
| 108 | + Next |
| 109 | + |
| 110 | + filterPersistenceElement.Element("Value").Element("string").Value = String.Join(",", newFilterStates.ToArray()) |
| 111 | + End If |
| 112 | + |
| 113 | + Return doc.ToString() |
| 114 | +End Function |
| 115 | + |
| 116 | +' Legacy code for deserializing the state |
| 117 | +Public Shared Function DeserializeObject(objectToDeserialize As String) As Object |
| 118 | + Dim unencodedArray As Byte() = Convert.FromBase64String(objectToDeserialize) |
| 119 | + Dim bf As New BinaryFormatter() |
| 120 | + Using ms As New MemoryStream(unencodedArray) |
| 121 | + Return bf.Deserialize(ms) |
| 122 | + End Using |
| 123 | +End Function |
| 124 | + |
| 125 | +' Current code for serializing the state |
| 126 | +Public Shared Function SerializeObject(objectToSerialize As Object) As String |
| 127 | + Dim jsonString As String = JsonConvert.SerializeObject(objectToSerialize) |
| 128 | + Dim bytes As Byte() = Encoding.UTF8.GetBytes(jsonString) |
| 129 | + Return Convert.ToBase64String(bytes) |
| 130 | +End Function |
| 131 | +```` |
| 132 | + |
| 133 | +You can create a few helper methods and plug-in the functions from above to handle different scenarios. |
| 134 | + |
| 135 | +````C# |
| 136 | +// Update the state in files |
| 137 | +private void UpdateStateInFile(string filePath) |
| 138 | +{ |
| 139 | + string oldState = GetFileContents(filePath); |
| 140 | + string newState = ConvertState(oldState); |
| 141 | + WriteFileContents(filePath, newState); |
| 142 | +} |
| 143 | + |
| 144 | +// Convert the old state from string to new state |
| 145 | +private string GetConvertedState(string oldState) |
| 146 | +{ |
| 147 | + return ConvertState(oldState); |
| 148 | +} |
| 149 | + |
| 150 | +private string GetFileContents(string filePath) |
| 151 | +{ |
| 152 | + string contents; |
| 153 | + using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) |
| 154 | + using (var reader = new StreamReader(stream)) |
| 155 | + { |
| 156 | + contents = reader.ReadToEnd(); |
| 157 | + } |
| 158 | + return contents; |
| 159 | +} |
| 160 | + |
| 161 | +private void WriteFileContents(string filePath, string contents) |
| 162 | +{ |
| 163 | + using (var stream = new FileStream(Server.MapPath(filePath), FileMode.Create, FileAccess.Write, FileShare.Read)) |
| 164 | + using (var writer = new StreamWriter(stream)) |
| 165 | + { |
| 166 | + writer.Write(contents); |
| 167 | + } |
| 168 | +} |
| 169 | +```` |
| 170 | +````VB.NET |
| 171 | +'Update the state in files |
| 172 | +Private Sub UpdateStateInFile(filePath As String) |
| 173 | + Dim oldState As String = GetFileContents(filePath) |
| 174 | + Dim newState As String = ConvertState(oldState) |
| 175 | + WriteFileContents(filePath, newState) |
| 176 | +End Sub |
| 177 | + |
| 178 | +'Convert the old state from string to new state |
| 179 | +Private Function GetConvertedState(oldState As String) As String |
| 180 | + Return ConvertState(oldState) |
| 181 | +End Function |
| 182 | + |
| 183 | +Private Function GetFileContents(filePath As String) As String |
| 184 | + Dim contents As String |
| 185 | + Using stream As New FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) |
| 186 | + Using reader As New StreamReader(stream) |
| 187 | + contents = reader.ReadToEnd() |
| 188 | + End Using |
| 189 | + End Using |
| 190 | + Return contents |
| 191 | +End Function |
| 192 | + |
| 193 | +Private Sub WriteFileContents(filePath As String, contents As String) |
| 194 | + Using stream As New FileStream(Server.MapPath(filePath), FileMode.Create, FileAccess.Write, FileShare.Read) |
| 195 | + Using writer As New StreamWriter(stream) |
| 196 | + writer.Write(contents) |
| 197 | + End Using |
| 198 | + End Using |
| 199 | +End Sub |
| 200 | +```` |
| 201 | + |
| 202 | + |
| 203 | +## See Also |
| 204 | + |
| 205 | +* [Persisting PivotGrid Settings](https://demos.telerik.com/aspnet-ajax/pivotgrid/examples/applicationscenarios/persisting-radpivotgrid-settings/defaultcs.aspx) |
0 commit comments