Skip to content

Commit 1fe7931

Browse files
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>
1 parent 16132b0 commit 1fe7931

File tree

2 files changed

+84
-2
lines changed

2 files changed

+84
-2
lines changed

src/Umbraco.Infrastructure/Serialization/JsonBlockValueConverter.cs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Text.Json;
2+
using System.Text.Json.Nodes;
23
using System.Text.Json.Serialization;
34
using Umbraco.Cms.Core.Models.Blocks;
45
using Umbraco.Extensions;
@@ -121,7 +122,17 @@ private static Type GetLayoutItemType(Type blockValueType)
121122
}
122123

123124
private List<BlockItemData> DeserializeBlockItemData(ref Utf8JsonReader reader, JsonSerializerOptions options, Type typeToConvert, string propertyName)
124-
=> DeserializeListOf<BlockItemData>(ref reader, options, typeToConvert, propertyName);
125+
{
126+
try
127+
{
128+
return DeserializeListOf<BlockItemData>(ref reader, options, typeToConvert, propertyName);
129+
}
130+
catch (JsonException ex) when (ex.Path?.EndsWith(".values") is true)
131+
{
132+
// If we hit a JsonException due to the "values" property conflict, attempt the fallback deserialization
133+
return FallbackBlockItemDataDeserialization(ref reader, options);
134+
}
135+
}
125136

126137
private List<BlockItemVariation> DeserializeBlockVariation(ref Utf8JsonReader reader, JsonSerializerOptions options, Type typeToConvert, string propertyName)
127138
=> DeserializeListOf<BlockItemVariation>(ref reader, options, typeToConvert, propertyName);
@@ -224,5 +235,38 @@ private void DeserializeAndSetLayout(ref Utf8JsonReader reader, JsonSerializerOp
224235
}
225236
}
226237
}
227-
}
228238

239+
[Obsolete("Only needed to support the old data schema. Remove in V18.")]
240+
private static List<BlockItemData> FallbackBlockItemDataDeserialization(ref Utf8JsonReader reader, JsonSerializerOptions options)
241+
{
242+
JsonArray? arrayElement = JsonSerializer.Deserialize<JsonArray>(ref reader, options);
243+
244+
return arrayElement?
245+
.Select(itemElement => DeserializeBlockItemData(itemElement, options))
246+
.OfType<BlockItemData>()
247+
.ToList() ?? [];
248+
}
249+
250+
[Obsolete("Only needed to support the old data schema. Remove in V18.")]
251+
private static BlockItemData? DeserializeBlockItemData(JsonNode? jsonNode, JsonSerializerOptions options)
252+
{
253+
if (jsonNode is not JsonObject jsonObject || jsonObject.ContainsKey("values") is false)
254+
{
255+
// Nothing to be done, just deserialize as usual
256+
return jsonNode.Deserialize<BlockItemData>(options);
257+
}
258+
259+
// Handle the "values" property conflict by extracting the "values" property first and adding it to the
260+
// RawPropertyValues dictionary after deserialization
261+
JsonNode? values = jsonObject["values"];
262+
jsonObject.Remove("values");
263+
264+
BlockItemData? blockItemData = jsonObject.Deserialize<BlockItemData>(options);
265+
if (blockItemData is not null)
266+
{
267+
blockItemData.RawPropertyValues["values"] = values.Deserialize<object?>(options);
268+
}
269+
270+
return blockItemData;
271+
}
272+
}

tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Serialization/JsonBlockValueConverterTests.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,4 +470,42 @@ public void Try_Deserialize_Unknown_Block_Layout_With_Nested_Array()
470470
var serializer = new SystemTextJsonSerializer(new DefaultJsonSerializerEncoderFactory());
471471
Assert.DoesNotThrow(() => serializer.Deserialize<BlockListValue>(json));
472472
}
473+
474+
/// <summary>
475+
/// Test case that verifies the fix for https://github.com/umbraco/Umbraco-CMS/issues/20409.
476+
/// </summary>
477+
[Test]
478+
public void Can_Deserialize_BlockGrid_With_Blocks_Using_Values_As_Property_Alias()
479+
{
480+
// Create a serialized BlockGridValue in Umbraco 13 format that has a block with a property alias "values".
481+
var serialized = @"{
482+
""layout"":{
483+
""Umbraco.BlockList"":[
484+
{
485+
""contentUdi"":""umb://element/6ad18441631140d48515ea0fc5b00425""
486+
}
487+
]
488+
},
489+
""contentData"":[
490+
{
491+
""contentTypeKey"":""a1d1123c-289b-4a05-b33f-9f06cb723da1"",
492+
""udi"":""umb://element/6ad18441631140d48515ea0fc5b00425"",
493+
""text"":""Text"",
494+
""values"":""Values""
495+
}
496+
],
497+
""settingsData"":[
498+
]
499+
}";
500+
501+
var serializer = new SystemTextJsonSerializer(new DefaultJsonSerializerEncoderFactory());
502+
var deserialized = serializer.Deserialize<BlockGridValue>(serialized);
503+
504+
Assert.IsNotNull(deserialized);
505+
506+
Assert.AreEqual(1, deserialized.ContentData.Count);
507+
Assert.AreEqual(2, deserialized.ContentData[0].RawPropertyValues.Count);
508+
Assert.AreEqual("Text", deserialized.ContentData[0].RawPropertyValues["text"]);
509+
Assert.AreEqual("Values", deserialized.ContentData[0].RawPropertyValues["values"]);
510+
}
473511
}

0 commit comments

Comments
 (0)