Migrations: Adjust the JsonBlockValueConverter to handle conflicts with 'values' property (#20429)
* Adjust the `JsonBlockValueConverter` to handle conflicts with 'values' property (due to old data schema) * Simplify code * Add unit test to verify change. --------- Co-authored-by: Andy Butland <abutland73@gmail.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using Umbraco.Cms.Core.Models.Blocks;
|
using Umbraco.Cms.Core.Models.Blocks;
|
||||||
using Umbraco.Extensions;
|
using Umbraco.Extensions;
|
||||||
@@ -121,7 +122,17 @@ public class JsonBlockValueConverter : JsonConverter<BlockValue>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<BlockItemData> DeserializeBlockItemData(ref Utf8JsonReader reader, JsonSerializerOptions options, Type typeToConvert, string propertyName)
|
private List<BlockItemData> DeserializeBlockItemData(ref Utf8JsonReader reader, JsonSerializerOptions options, Type typeToConvert, string propertyName)
|
||||||
=> DeserializeListOf<BlockItemData>(ref reader, options, typeToConvert, propertyName);
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return DeserializeListOf<BlockItemData>(ref reader, options, typeToConvert, propertyName);
|
||||||
|
}
|
||||||
|
catch (JsonException ex) when (ex.Path?.EndsWith(".values") is true)
|
||||||
|
{
|
||||||
|
// If we hit a JsonException due to the "values" property conflict, attempt the fallback deserialization
|
||||||
|
return FallbackBlockItemDataDeserialization(ref reader, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<BlockItemVariation> DeserializeBlockVariation(ref Utf8JsonReader reader, JsonSerializerOptions options, Type typeToConvert, string propertyName)
|
private List<BlockItemVariation> DeserializeBlockVariation(ref Utf8JsonReader reader, JsonSerializerOptions options, Type typeToConvert, string propertyName)
|
||||||
=> DeserializeListOf<BlockItemVariation>(ref reader, options, typeToConvert, propertyName);
|
=> DeserializeListOf<BlockItemVariation>(ref reader, options, typeToConvert, propertyName);
|
||||||
@@ -224,5 +235,38 @@ public class JsonBlockValueConverter : JsonConverter<BlockValue>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
[Obsolete("Only needed to support the old data schema. Remove in V18.")]
|
||||||
|
private static List<BlockItemData> FallbackBlockItemDataDeserialization(ref Utf8JsonReader reader, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
JsonArray? arrayElement = JsonSerializer.Deserialize<JsonArray>(ref reader, options);
|
||||||
|
|
||||||
|
return arrayElement?
|
||||||
|
.Select(itemElement => DeserializeBlockItemData(itemElement, options))
|
||||||
|
.OfType<BlockItemData>()
|
||||||
|
.ToList() ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Only needed to support the old data schema. Remove in V18.")]
|
||||||
|
private static BlockItemData? DeserializeBlockItemData(JsonNode? jsonNode, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (jsonNode is not JsonObject jsonObject || jsonObject.ContainsKey("values") is false)
|
||||||
|
{
|
||||||
|
// Nothing to be done, just deserialize as usual
|
||||||
|
return jsonNode.Deserialize<BlockItemData>(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the "values" property conflict by extracting the "values" property first and adding it to the
|
||||||
|
// RawPropertyValues dictionary after deserialization
|
||||||
|
JsonNode? values = jsonObject["values"];
|
||||||
|
jsonObject.Remove("values");
|
||||||
|
|
||||||
|
BlockItemData? blockItemData = jsonObject.Deserialize<BlockItemData>(options);
|
||||||
|
if (blockItemData is not null)
|
||||||
|
{
|
||||||
|
blockItemData.RawPropertyValues["values"] = values.Deserialize<object?>(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return blockItemData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -470,4 +470,42 @@ public class JsonBlockValueConverterTests
|
|||||||
var serializer = new SystemTextJsonSerializer(new DefaultJsonSerializerEncoderFactory());
|
var serializer = new SystemTextJsonSerializer(new DefaultJsonSerializerEncoderFactory());
|
||||||
Assert.DoesNotThrow(() => serializer.Deserialize<BlockListValue>(json));
|
Assert.DoesNotThrow(() => serializer.Deserialize<BlockListValue>(json));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test case that verifies the fix for https://github.com/umbraco/Umbraco-CMS/issues/20409.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void Can_Deserialize_BlockGrid_With_Blocks_Using_Values_As_Property_Alias()
|
||||||
|
{
|
||||||
|
// Create a serialized BlockGridValue in Umbraco 13 format that has a block with a property alias "values".
|
||||||
|
var serialized = @"{
|
||||||
|
""layout"":{
|
||||||
|
""Umbraco.BlockList"":[
|
||||||
|
{
|
||||||
|
""contentUdi"":""umb://element/6ad18441631140d48515ea0fc5b00425""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
""contentData"":[
|
||||||
|
{
|
||||||
|
""contentTypeKey"":""a1d1123c-289b-4a05-b33f-9f06cb723da1"",
|
||||||
|
""udi"":""umb://element/6ad18441631140d48515ea0fc5b00425"",
|
||||||
|
""text"":""Text"",
|
||||||
|
""values"":""Values""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
""settingsData"":[
|
||||||
|
]
|
||||||
|
}";
|
||||||
|
|
||||||
|
var serializer = new SystemTextJsonSerializer(new DefaultJsonSerializerEncoderFactory());
|
||||||
|
var deserialized = serializer.Deserialize<BlockGridValue>(serialized);
|
||||||
|
|
||||||
|
Assert.IsNotNull(deserialized);
|
||||||
|
|
||||||
|
Assert.AreEqual(1, deserialized.ContentData.Count);
|
||||||
|
Assert.AreEqual(2, deserialized.ContentData[0].RawPropertyValues.Count);
|
||||||
|
Assert.AreEqual("Text", deserialized.ContentData[0].RawPropertyValues["text"]);
|
||||||
|
Assert.AreEqual("Values", deserialized.ContentData[0].RawPropertyValues["values"]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user