Reputation: 2830
Consider the following class:
public class ImageDataModel
{
public JsonObject CaptureResultData { get; }
public SmartphoneCommand SmartphoneCommand { get; }
public LightStageCommand LightStageCommand { get; }
public string TimeStamp { get; }
public ImageDataModel(string _captureResultData, LightStageCommand _lightStageCommand, SmartphoneCommand _smartphoneCommand)
{
CaptureResultData = JsonObject.Parse(_captureResultData);
SmartphoneCommand = _smartphoneCommand;
LightStageCommand = _lightStageCommand;
TimeStamp = DateTime.Now.ToString("HH:mm.ss, dd. MM. yyyy");
}
}
SmartphoneCommand
and LightStageCommand
are serializable objects, no problem with those. However, in the constructor, _captureResultData
is already a serialized JSON string of type JsonObject
. Since I want the data in the this string to show up as serialized object data instead of a single string within my JSON file, I made it into a JsonObject
.
The problem is, after serialization, the CaptureResult data shows up in the JSON file as follows:
"CaptureResultData": {
"android.control.afMode": {
"ValueType": 2
},
"android.colorCorrection.gains": {
"ValueType": 3
},
"android.control.awbMode": {
"ValueType": 2
},
"android.lens.focalLength": {
"ValueType": 2
},
"android.lens.focusDistance": {
"ValueType": 2
},
"android.control.aeMode": {
"ValueType": 2
},
"android.colorCorrection.mode": {
"ValueType": 2
},
"android.colorCorrection.transform": {
"ValueType": 3
},
"android.lens.aperture": {
"ValueType": 2
},
"android.sensor.sensitivity": {
"ValueType": 2
},
"android.sensor.exposureTime": {
"ValueType": 2
}
},
The original string contains the correct data. How can I force the serialization to show the actual data, instead of ValueType
?
For completeness, here is how the serialization is done:
using (StreamWriter jsonFile = File.CreateText(uniqueFilePaths[2]))
{
JsonSerializer serializer = new JsonSerializer
{
Formatting = Formatting.Indented,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
serializer.Serialize(jsonFile, new ImageDataModel(captureResultString, lightStageCommand, cameraCommand));
}
Upvotes: 1
Views: 242
Reputation: 116669
Json.NET does not have any support for the types in the Windows.Data.Json
namespace, so you will need to create a custom JsonConverter
for JsonObject
as well as JsonArray
and JsonValue
if you ever use those classes directly. The following should do the job:
public class WindowsDataJsonObjectConverter : WindowsDataJsonConverterBase<Windows.Data.Json.JsonObject>
{
public override JsonObject ReadJson(JsonReader reader, Type objectType, JsonObject existingValue, bool hasExistingValue, JsonSerializer serializer) =>
JsonObject.Parse(reader.ReadOuterJson(dateParseHandling: DateParseHandling.None));
}
public class WindowsDataJsonArrayConverter : WindowsDataJsonConverterBase<Windows.Data.Json.JsonArray>
{
public override JsonArray ReadJson(JsonReader reader, Type objectType, JsonArray existingValue, bool hasExistingValue, JsonSerializer serializer) =>
JsonArray.Parse(reader.ReadOuterJson(dateParseHandling: DateParseHandling.None));
}
public class WindowsDataJsonValueConverter : WindowsDataJsonConverterBase<Windows.Data.Json.JsonValue>
{
public override JsonValue ReadJson(JsonReader reader, Type objectType, JsonValue existingValue, bool hasExistingValue, JsonSerializer serializer) =>
JsonValue.Parse(reader.ReadOuterJson(dateParseHandling: DateParseHandling.None));
}
public abstract class WindowsDataJsonConverterBase<TJsonValue> : JsonConverter<TJsonValue> where TJsonValue : IJsonValue
{
public override void WriteJson(JsonWriter writer, TJsonValue value, JsonSerializer serializer) =>
writer.WriteRawValue(value.Stringify());
}
public static partial class JsonExtensions
{
// Taken from this answer https://stackoverflow.com/a/56945050/3744182
// To https://stackoverflow.com/questions/56944160/efficiently-get-full-json-string-in-jsonconverter-readjson
public static string ReadOuterJson(this JsonReader reader, Formatting formatting = Formatting.None, DateParseHandling? dateParseHandling = null, FloatParseHandling? floatParseHandling = null)
{
var oldDateParseHandling = reader.DateParseHandling;
var oldFloatParseHandling = reader.FloatParseHandling;
try
{
if (dateParseHandling != null)
reader.DateParseHandling = dateParseHandling.Value;
if (floatParseHandling != null)
reader.FloatParseHandling = floatParseHandling.Value;
using (var sw = new StringWriter(CultureInfo.InvariantCulture))
using (var jsonWriter = new JsonTextWriter(sw) { Formatting = formatting })
{
jsonWriter.WriteToken(reader);
return sw.ToString();
}
}
finally
{
reader.DateParseHandling = oldDateParseHandling;
reader.FloatParseHandling = oldFloatParseHandling;
}
}
}
Notes:
JsonObject
implements IDictionary<String,IJsonValue>
and JsonArray
implements several IEnumerable
interfaces, so Json.NET will know how to serialize these types. it will not, however, know to deserialize them by calling the appropriate static Parse()
methods.
There is no standard interface to indicate that a c# type should be serialized to JSON using its "raw" ToString()
value, so Json.NET has no way to know how to serialize or deserialize JsonValue
.
Alternatively, you could consider replacing JsonObject
with Json.NET's JObject
in your model. If you do, Json.NET will be able to serialize it without any conversion.
As a second alternative, you could leave _captureResultData
as a string
in your data model, and mark the property with [JsonConverter(typeof(RawConverter))]
where RawConverter
comes from this answer to How can I serialize and deserialize a type with a string member that contains "raw" JSON, without escaping the JSON in the process.
Since ImageDataModel
is immutable, if you want to deserialize it with Json.NET you will need to create a compatible constructor and mark it with JsonConstructorAttribute
:
[JsonConstructor]
ImageDataModel(JsonObject captureResultData, LightStageCommand lightStageCommand, SmartphoneCommand smartphoneCommand, string timeStamp)
{
this.CaptureResultData = captureResultData;
this.SmartphoneCommand = smartphoneCommand;
this.LightStageCommand = lightStageCommand;
this.TimeStamp = timeStamp;
}
Json.NET will match the JSON properties to constructor arguments using a case-invariant name match.
Mockup fiddle here.
Upvotes: 1