【问题标题】:How to serialize class to JSON, where property is of type Windows.Data.Json.JsonObject?如何将类序列化为 JSON,其中属性类型为 Windows.Data.Json.JsonObject?
【发布时间】:2021-06-01 09:02:44
【问题描述】:

考虑以下类:

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");
    }
}

SmartphoneCommandLightStageCommand是可序列化的对象,这些都没有问题。但是,在构造函数中,_captureResultData 已经是 JsonObject 类型的序列化 JSON 字符串。由于我希望此字符串中的数据显示为序列化对象数据,而不是 JSON 文件中的单个字符串,因此我将其设置为 JsonObject

问题是,序列化后,CaptureResult 数据在 JSON 文件中显示如下:

  "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
    }
  },

原始字符串包含正确的数据。如何强制序列化显示实际数据,而不是 ValueType

为了完整起见,以下是序列化的完成方式:

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));
}

【问题讨论】:

  • 我目前对该问题的解决方案是将属性设为字典:CaptureResultData = JsonConvert.DeserializeObject<Dictionary<string, string>>(_captureResultData);。它有效,但我想这只是一个不必要的额外转换步骤,所以我不喜欢它。
  • 您已标记此json.net,但JsonObject 是什么?它似乎不是该库的一部分。
  • @dbc 不,但 newtonsoft 是 json.net。
  • 我知道 Newtonsoft 是 Json.NET。但是为了帮助您,我们需要知道JsonObject 是什么。是System.Json.JsonObject吗?
  • 哦,对不起 - 我误会了。我目前不在我的工作机器上,但由于我使用的是Parse() 函数,它应该是Windows.Data.Json.JsonObject

标签: c# json json.net


【解决方案1】:

Json.NET 不支持 Windows.Data.Json 命名空间中的类型,因此您需要为 JsonObject 以及 JsonArrayJsonValue 创建一个 custom JsonConverter(如果您曾经使用过这些类型)直接上课。以下应该可以完成这项工作:

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;
        }
    }
}

注意事项:

  • JsonObject 实现了IDictionary&lt;String,IJsonValue&gt;JsonArray 实现了几个IEnumerable 接口,所以Json.NET 会知道如何序列化这些类型。但是,它不会知道通过调用适当的静态 Parse() 方法来反序列化它们。

  • 没有标准接口表明 ac# 类型应使用其“原始”ToString() 值序列化为 JSON,因此 Json.NET 无法知道如何序列化或反序列化 JsonValue

  • 或者,您可以考虑将模型中的 JsonObject 替换为 Json.NET 的 JObject。如果你这样做了,Json.NET 将能够在不进行任何转换的情况下对其进行序列化。

    作为第二种选择,您可以将_captureResultData 保留为数据模型中的string,并用[JsonConverter(typeof(RawConverter))] 标记属性,其中RawConverter 来自this answerHow can I serialize and deserialize a type with a string member that contains "raw" JSON, without escaping the JSON in the process em>。

  • 由于ImageDataModel 是不可变的,如果你想用Json.NET 反序列化它,你需要创建一个兼容的构造函数并用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 将使用不区分大小写的名称匹配将 JSON 属性与构造函数参数匹配。

样机小提琴here.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-17
    • 2023-03-28
    相关资源
    最近更新 更多