Skip to content

Commit 9861c0c

Browse files
committed
Replace Obsolete BinaryFormatter with System.Text.Json
Replace BinaryFormatter with System.Text.Json serialization Switched serialization in UncertaintyAnalysisResults, MCMCResults, ParameterResults, and related classes from BinaryFormatter to System.Text.Json for improved security and compatibility. Added JsonInclude attributes to private setters for correct serialization. Updated project file to include System.Text.Json package and removed obsolete [Serializable] attributes. Reimplement binary formatter for reading legacy saved data.
1 parent a8b8d9a commit 9861c0c

6 files changed

Lines changed: 1019 additions & 28 deletions

File tree

Numerics/Distributions/Univariate/Uncertainty Analysis/UncertaintyAnalysisResults.cs

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,14 @@
3030

3131
using Numerics.Mathematics.Optimization;
3232
using Numerics.Sampling.MCMC;
33+
using Numerics.Utilities;
3334
using System;
3435
using System.Collections.Generic;
3536
using System.Globalization;
3637
using System.IO;
3738
using System.Linq;
38-
using System.Runtime.Serialization.Formatters.Binary;
39+
using System.Text.Json;
40+
using System.Text.Json.Serialization;
3941
using System.Threading.Tasks;
4042
using System.Xml.Linq;
4143

@@ -51,7 +53,6 @@ namespace Numerics.Distributions
5153
/// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil
5254
/// </para>
5355
/// </remarks>
54-
[Serializable]
5556
public class UncertaintyAnalysisResults
5657
{
5758

@@ -111,30 +112,66 @@ public class UncertaintyAnalysisResults
111112
/// <param name="results">The uncertainty analysis results.</param>
112113
public static byte[] ToByteArray(UncertaintyAnalysisResults results)
113114
{
114-
BinaryFormatter bf = new BinaryFormatter();
115-
using (var ms = new MemoryStream())
115+
var options = new JsonSerializerOptions
116116
{
117-
bf.Serialize(ms, results);
118-
return ms.ToArray();
119-
}
117+
WriteIndented = false,
118+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
119+
IncludeFields = true
120+
};
121+
// Add custom converters for unsupported types
122+
options.Converters.Add(new Double2DArrayConverter());
123+
options.Converters.Add(new String2DArrayConverter());
124+
options.Converters.Add(new UnivariateDistributionConverter());
125+
return JsonSerializer.SerializeToUtf8Bytes(results, options);
120126
}
121127

122128
/// <summary>
123129
/// Returns the class from a byte array.
124130
/// </summary>
125131
/// <param name="bytes">Byte array.</param>
126132
public static UncertaintyAnalysisResults FromByteArray(byte[] bytes)
133+
{
134+
try
135+
{
136+
var options = new JsonSerializerOptions
137+
{
138+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
139+
IncludeFields = true
140+
};
141+
// Add custom converters for unsupported types
142+
options.Converters.Add(new Double2DArrayConverter());
143+
options.Converters.Add(new String2DArrayConverter());
144+
options.Converters.Add(new UnivariateDistributionConverter());
145+
return JsonSerializer.Deserialize<UncertaintyAnalysisResults>(bytes, options);
146+
}
147+
catch (Exception)
148+
{
149+
// An error can occur because we're trying to deserialize a blob written with binary formatter,
150+
//as a blob of json bytes. If that happens, fall back to the old.
151+
return FromByteArrayLegacy(bytes);
152+
}
153+
}
154+
155+
/// <summary>
156+
/// Returns the class from a byte array.
157+
/// </summary>
158+
/// <param name="bytes">Byte array.</param>
159+
private static UncertaintyAnalysisResults FromByteArrayLegacy(byte[] bytes)
127160
{
128161
try
129162
{
130163
using (var memStream = new MemoryStream())
131164
{
132-
var binForm = new BinaryFormatter();
165+
#pragma warning disable SYSLIB0011 // Suppress obsolete BinaryFormatter warning for legacy support
166+
var binForm = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
133167
memStream.Write(bytes, 0, bytes.Length);
134168
memStream.Seek(0, SeekOrigin.Begin);
135169
var obj = binForm.Deserialize(memStream);
170+
#pragma warning disable SYSLIB0011 // Suppress obsolete BinaryFormatter warning for legacy support
136171
return (UncertaintyAnalysisResults)obj;
137172
}
173+
174+
138175
}
139176
catch (Exception)
140177
{

Numerics/Sampling/MCMC/Support/MCMCResults.cs

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
using System.Collections.Generic;
3434
using System.IO;
3535
using System.Linq;
36-
using System.Runtime.Serialization.Formatters.Binary;
36+
using System.Text.Json;
37+
using System.Text.Json.Serialization;
3738

3839
namespace Numerics.Sampling.MCMC
3940
{
@@ -46,7 +47,7 @@ namespace Numerics.Sampling.MCMC
4647
/// Haden Smith, USACE Risk Management Center, cole.h.smith@usace.army.mil
4748
/// </para>
4849
/// </remarks>
49-
[Serializable]
50+
[Serializable]
5051
public class MCMCResults
5152
{
5253
/// <summary>
@@ -78,37 +79,44 @@ public MCMCResults(MCMCSampler sampler, double alpha = 0.1)
7879
/// <summary>
7980
/// The list of sampled Markov Chains.
8081
/// </summary>
82+
[JsonInclude]
8183
public List<ParameterSet>[] MarkovChains { get; private set; }
8284

8385
/// <summary>
84-
/// Output posterior parameter sets.
86+
/// Output posterior parameter sets.
8587
/// </summary>
88+
[JsonInclude]
8689
public List<ParameterSet> Output { get; private set; }
8790

8891
/// <summary>
8992
/// The average log-likelihood across each chain for each iteration.
9093
/// </summary>
94+
[JsonInclude]
9195
public List<double> MeanLogLikelihood { get; private set; }
9296

9397
/// <summary>
9498
/// The acceptance rate for each chain.
9599
/// </summary>
100+
[JsonInclude]
96101
public double[] AcceptanceRates { get; private set; }
97102

98103
/// <summary>
99104
/// Parameter results using the output posterior parameter sets.
100105
/// </summary>
106+
[JsonInclude]
101107
public ParameterResults[] ParameterResults { get; private set; }
102108

103109
/// <summary>
104-
/// The output parameter set that produced the maximum likelihood.
105-
/// This is referred to as the maximum a posteriori (MAP).
110+
/// The output parameter set that produced the maximum likelihood.
111+
/// This is referred to as the maximum a posteriori (MAP).
106112
/// </summary>
113+
[JsonInclude]
107114
public ParameterSet MAP { get; private set; }
108115

109116
/// <summary>
110-
/// The mean of the posterior distribution of each parameter.
117+
/// The mean of the posterior distribution of each parameter.
111118
/// </summary>
119+
[JsonInclude]
112120
public ParameterSet PosteriorMean { get; private set; }
113121

114122
/// <summary>
@@ -147,12 +155,13 @@ private void ProcessParameterResults(MCMCSampler sampler, double alpha = 0.1)
147155
/// <param name="mcmcResults">The MCMC Results.</param>
148156
public static byte[] ToByteArray(MCMCResults mcmcResults)
149157
{
150-
var bf = new BinaryFormatter();
151-
using (var ms = new MemoryStream())
158+
var options = new JsonSerializerOptions
152159
{
153-
bf.Serialize(ms, mcmcResults);
154-
return ms.ToArray();
155-
}
160+
WriteIndented = false,
161+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
162+
IncludeFields = true
163+
};
164+
return JsonSerializer.SerializeToUtf8Bytes(mcmcResults, options);
156165
}
157166

158167
/// <summary>
@@ -161,16 +170,39 @@ public static byte[] ToByteArray(MCMCResults mcmcResults)
161170
/// <param name="bytes">Byte array.</param>
162171
public static MCMCResults FromByteArray(byte[] bytes)
163172
{
164-
using (var ms = new MemoryStream())
173+
var options = new JsonSerializerOptions
174+
{
175+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
176+
IncludeFields = true
177+
};
178+
try
179+
{
180+
return JsonSerializer.Deserialize<MCMCResults>(bytes, options);
181+
}
182+
catch
165183
{
166-
var bf = new BinaryFormatter();
167-
ms.Write(bytes, 0, bytes.Length);
168-
ms.Seek(0L, SeekOrigin.Begin);
169-
var obj = bf.Deserialize(ms);
170-
return (MCMCResults)obj;
184+
///Previous serialization used Binary Formatter, which won't deserialize cleanly as JSON.
185+
/// If this fails, then it's probably the bf bytes. fall back to legacy.
186+
return FromByteArrayLegacy(bytes);
171187
}
172188
}
173189

190+
/// <summary>
191+
/// Creates MCMC Results from a byte array.
192+
/// </summary>
193+
/// <param name="bytes">Byte array.</param>
194+
private static MCMCResults FromByteArrayLegacy(byte[] bytes)
195+
{
196+
using var ms = new MemoryStream();
197+
#pragma warning disable SYSLIB0011 // Suppress obsolete BinaryFormatter warning for legacy support
198+
var bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
199+
ms.Write(bytes, 0, bytes.Length);
200+
ms.Seek(0L, SeekOrigin.Begin);
201+
var obj = bf.Deserialize(ms);
202+
#pragma warning disable SYSLIB0011 // Suppress obsolete BinaryFormatter warning for legacy support
203+
return (MCMCResults)obj;
204+
}
205+
174206
#endregion
175207

176208
}

Numerics/Sampling/MCMC/Support/ParameterResults.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
using Numerics.Data.Statistics;
3232
using Numerics.Distributions;
3333
using System;
34+
using System.Text.Json.Serialization;
3435

3536
namespace Numerics.Sampling.MCMC
3637
{
@@ -57,11 +58,11 @@ public class ParameterResults
5758
public ParameterResults(double[] values, double alpha = 0.1, bool sorted = false)
5859
{
5960
// Sort the values
60-
if (sorted == false)
61-
{
61+
if (sorted == false)
62+
{
6263
Array.Sort(values);
6364
}
64-
65+
6566
// Create Kernel Density Estimate
6667
var kde = new KernelDensity(values);
6768
KernelDensity = kde.CreatePDFGraph();
@@ -83,16 +84,19 @@ public ParameterResults(double[] values, double alpha = 0.1, bool sorted = false
8384
/// <summary>
8485
/// Parameter summary statistics.
8586
/// </summary>
87+
[JsonInclude]
8688
public ParameterStatistics SummaryStatistics { get; private set; }
8789

8890
/// <summary>
8991
/// The kernel density results.
9092
/// </summary>
93+
[JsonInclude]
9194
public double[,] KernelDensity { get; private set; }
9295

9396
/// <summary>
9497
/// The histogram results.
9598
/// </summary>
99+
[JsonInclude]
96100
public Histogram Histogram { get; private set; }
97101

98102
/// <summary>

0 commit comments

Comments
 (0)