using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Serialization; using Umbraco.Extensions; namespace Umbraco.Cms.Core.PropertyEditors; /// /// Abstract base for property index value factories where the value is json. /// /// The type to deserialize the json to. public abstract class JsonPropertyIndexValueFactoryBase : IPropertyIndexValueFactory { private readonly IJsonSerializer _jsonSerializer; private IndexingSettings _indexingSettings; protected bool ForceExplicitlyIndexEachNestedProperty { get; set; } /// /// Constructor for the JsonPropertyIndexValueFactoryBase. /// protected JsonPropertyIndexValueFactoryBase(IJsonSerializer jsonSerializer, IOptionsMonitor indexingSettings) { _jsonSerializer = jsonSerializer; _indexingSettings = indexingSettings.CurrentValue; indexingSettings.OnChange(newValue => _indexingSettings = newValue); } public virtual IEnumerable>> GetIndexValues( IProperty property, string? culture, string? segment, bool published, IEnumerable availableCultures, IDictionary contentTypeDictionary) { var result = new List>>(); var propertyValue = property.GetValue(culture, segment, published); // If there is a value, it's a string and it's detected as json. if (propertyValue is string rawValue && rawValue.DetectIsJson()) { try { TSerialized? deserializedPropertyValue = _jsonSerializer.Deserialize(rawValue); if (deserializedPropertyValue is null) { return result; } result.AddRange(Handle(deserializedPropertyValue, property, culture, segment, published, availableCultures, contentTypeDictionary)); } catch (InvalidCastException) { // Swallow...on purpose, there's a chance that this isn't the json format we are looking for // and we don't want that to affect the website. } catch (ArgumentException) { // Swallow on purpose to prevent this error: // Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject. } } IEnumerable>> summary = HandleResume(result, property, culture, segment, published); if (_indexingSettings.ExplicitlyIndexEachNestedProperty || ForceExplicitlyIndexEachNestedProperty) { result.AddRange(summary); return result; } return summary; } /// /// Method to return a list of summary of the content. By default this returns an empty list /// protected virtual IEnumerable>> HandleResume( List>> result, IProperty property, string? culture, string? segment, bool published) => Array.Empty>>(); /// /// Method that handle the deserialized object. /// protected abstract IEnumerable>> Handle( TSerialized deserializedPropertyValue, IProperty property, string? culture, string? segment, bool published, IEnumerable availableCultures, IDictionary contentTypeDictionary); }