System.Text.Json 中是否有 JRaw 的等效项

Is there an equivalent for JRaw in System.Text.Json

我正在将 ASP.NET Core 2 应用程序迁移到 ASP.NET Core 3。我的控制器需要 return 具有 属性 的对象已经是 JSON 字符串,所以它看起来像这样:

public class Thing {
    public int Id { get; set; }
    public string Name { get; set; }
    public string Data { get; set; } // JSON Data
}

var thing = new Thing
{
    Id = 1,
    Name = "Thing",
    Data = "{\"key\":\"value\"}"
};

Thing 应该被序列化,这样 Data 属性 就不是字符串,而是 JSON 对象的一部分。像这样:

{
    "id": 1,
    "name": "Thing",
    "data": {
        "key": "value"
    }
}

在 .NET Core 2 和 Newtonsoft.Json 中,我使用 JRaw 作为 Data 的类型,因此序列化程序知道 属性 已经序列化为 JSON 并没有尝试将其表示为字符串。

.NET Core 3 使用 System.Text.Json,与 JRaw 不兼容。有没有做同样事情的等价物?我能够使用 JsonElement 作为 Data 的类型并使用 JsonDocument.Parse(jsonString).RootElement 转换字符串。这会产生所需的结果,但我想避免不必要的反序列化+序列化步骤,因为数据对象可能相对较大。

.NET 6 引入了 Utf8JsonWriter.WriteRawValue(string json, bool skipInputValidation = false):

Writes the input as JSON content. It is expected that the input content is a single complete JSON value. ...

When writing untrusted JSON values, do not set skipInputValidation to true as this can result in invalid JSON being written, or an invalid overall payload being written to the writer instance.

因此您现在可以引入以下转换器:

/// <summary>
/// Serializes the contents of a string value as raw JSON.  The string is validated as being an RFC 8259-compliant JSON payload
/// </summary>
public class RawJsonConverter : JsonConverter<string>
{
    public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        using var doc = JsonDocument.ParseValue(ref reader);
        return doc.RootElement.GetRawText();
    }

    protected virtual bool SkipInputValidation => false;

    public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) =>
        // skipInputValidation : true will improve performance, but only do this if you are certain the value represents well-formed JSON!
        writer.WriteRawValue(value, skipInputValidation : SkipInputValidation);
}

/// <summary>
/// Serializes the contents of a string value as raw JSON.  The string is NOT validated as being an RFC 8259-compliant JSON payload
/// </summary>
public class UnsafeRawJsonConverter : RawJsonConverter
{
    protected override bool SkipInputValidation => true;
}

并将您选择的转换器应用于您的数据模型:

public class Thing {
    public int Id { get; set; }
    public string Name { get; set; }
    [JsonConverter(typeof(UnsafeRawJsonConverter))]
    public string Data { get; set; } // JSON Data
}

备注:

  • Utf8JsonReader 似乎没有等效的方法来读取任何类型的原始值,因此仍然需要 JsonDocument 来反序列化原始 JSON值。

  • Utf8JsonWriter.WriteRawValue(null)Utf8JsonWriter.WriteRawValue("null")生成相同的JSON,即null。反序列化时,doc.RootElement.GetRawText() 似乎 return 两者都是空值,而不是包含标记 null.

    的字符串
  • 您似乎很在意性能,所以我将 UnsafeRawJsonConverter 应用于您的数据模型。但是,只有在确定 Data 包含格式正确的 JSON 时,才应使用此转换器。如果不是,请使用 RawJsonConverter:

     [JsonConverter(typeof(RawJsonConverter))]
     public string Data { get; set; } // JSON Data
    

演示 fiddle here.