public void GivenACacheModel_WhenItsSerializedAndDeserializedWithAnySerializer_TheResultsAreTheSame() { var jsonSerializer = new JsonContentNestedDataSerializer(); var msgPackSerializer = new MsgPackContentNestedDataSerializer(Mock.Of <IPropertyCacheCompression>()); var now = DateTime.Now; var cacheModel = new ContentCacheDataModel { PropertyData = new Dictionary <string, PropertyData[]> { ["propertyOne"] = new[] { new PropertyData { Culture = "en-US", Segment = "test", Value = "hello world" } }, ["propertyTwo"] = new[] { new PropertyData { Culture = "en-US", Segment = "test", Value = "Lorem ipsum" } } }, CultureData = new Dictionary <string, CultureVariation> { ["en-US"] = new CultureVariation { Date = now, IsDraft = false, Name = "Home", UrlSegment = "home" } }, UrlSegment = "home" }; var content = Mock.Of <IReadOnlyContentBase>(x => x.ContentTypeId == 1); var json = jsonSerializer.Serialize(content, cacheModel, false).StringData; var msgPack = msgPackSerializer.Serialize(content, cacheModel, false).ByteData; Console.WriteLine(json); Console.WriteLine(msgPackSerializer.ToJson(msgPack)); var jsonContent = jsonSerializer.Deserialize(content, json, null, false); var msgPackContent = msgPackSerializer.Deserialize(content, null, msgPack, false); CollectionAssert.AreEqual(jsonContent.CultureData.Keys, msgPackContent.CultureData.Keys); CollectionAssert.AreEqual(jsonContent.PropertyData.Keys, msgPackContent.PropertyData.Keys); CollectionAssert.AreEqual(jsonContent.CultureData.Values, msgPackContent.CultureData.Values, new CultureVariationComparer()); CollectionAssert.AreEqual(jsonContent.PropertyData.Values, msgPackContent.PropertyData.Values, new PropertyDataComparer()); Assert.AreEqual(jsonContent.UrlSegment, msgPackContent.UrlSegment); }
/// <summary> /// Used during serialization to compress properties /// </summary> /// <param name="content"></param> /// <param name="model"></param> /// <param name="published"></param> /// <remarks> /// This will essentially 'double compress' property data. The MsgPack data as a whole will already be compressed /// but this will go a step further and double compress property data so that it is stored in the nucache file /// as compressed bytes and therefore will exist in memory as compressed bytes. That is, until the bytes are /// read/decompressed as a string to be displayed on the front-end. This allows for potentially a significant /// memory savings but could also affect performance of first rendering pages while decompression occurs. /// </remarks> private void Compress(IReadOnlyContentBase content, ContentCacheDataModel model, bool published) { if (model.PropertyData is null) { return; } foreach (KeyValuePair <string, PropertyData[]> propertyAliasToData in model.PropertyData) { if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key, published)) { foreach (PropertyData property in propertyAliasToData.Value.Where(x => x.Value != null && x.Value is string)) { if (property.Value is string propertyValue) { property.Value = LZ4Pickler.Pickle(Encoding.UTF8.GetBytes(propertyValue)); } } foreach (PropertyData property in propertyAliasToData.Value.Where(x => x.Value != null && x.Value is int intVal)) { property.Value = Convert.ToBoolean((int?)property.Value); } } } }
/// <summary> /// Used during deserialization to map the property data as lazy or expand the value /// </summary> /// <param name="content"></param> /// <param name="nestedData"></param> /// <param name="published"></param> private void Expand(IReadOnlyContentBase content, ContentCacheDataModel nestedData, bool published) { if (nestedData.PropertyData is null) { return; } foreach (KeyValuePair <string, PropertyData[]> propertyAliasToData in nestedData.PropertyData) { if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key, published)) { foreach (PropertyData property in propertyAliasToData.Value.Where(x => x.Value != null)) { if (property.Value is byte[] byteArrayValue) { property.Value = new LazyCompressedString(byteArrayValue); } } } } }
private ContentNuDto GetDto(IContentBase content, bool published, IContentCacheDataSerializer serializer) { // should inject these in ctor // BUT for the time being we decide not to support ConvertDbToXml/String // var propertyEditorResolver = PropertyEditorResolver.Current; // var dataTypeService = ApplicationContext.Current.Services.DataTypeService; var propertyData = new Dictionary <string, PropertyData[]>(); foreach (IProperty prop in content.Properties) { var pdatas = new List <PropertyData>(); foreach (IPropertyValue pvalue in prop.Values) { // sanitize - properties should be ok but ... never knows if (!prop.PropertyType.SupportsVariation(pvalue.Culture, pvalue.Segment)) { continue; } // note: at service level, invariant is 'null', but here invariant becomes 'string.Empty' var value = published ? pvalue.PublishedValue : pvalue.EditedValue; if (value != null) { pdatas.Add(new PropertyData { Culture = pvalue.Culture ?? string.Empty, Segment = pvalue.Segment ?? string.Empty, Value = value }); } } propertyData[prop.Alias] = pdatas.ToArray(); } var cultureData = new Dictionary <string, CultureVariation>(); // sanitize - names should be ok but ... never knows if (content.ContentType.VariesByCulture()) { ContentCultureInfosCollection infos = content is IContent document ? published ? document.PublishCultureInfos : document.CultureInfos : content.CultureInfos; // ReSharper disable once UseDeconstruction foreach (ContentCultureInfos cultureInfo in infos) { var cultureIsDraft = !published && content is IContent d && d.IsCultureEdited(cultureInfo.Culture); cultureData[cultureInfo.Culture] = new CultureVariation { Name = cultureInfo.Name, UrlSegment = content.GetUrlSegment(_shortStringHelper, _urlSegmentProviders, cultureInfo.Culture), Date = content.GetUpdateDate(cultureInfo.Culture) ?? DateTime.MinValue, IsDraft = cultureIsDraft }; } } // the dictionary that will be serialized var contentCacheData = new ContentCacheDataModel { PropertyData = propertyData, CultureData = cultureData, UrlSegment = content.GetUrlSegment(_shortStringHelper, _urlSegmentProviders) }; var serialized = serializer.Serialize(ReadOnlyContentBaseAdapter.Create(content), contentCacheData, published); var dto = new ContentNuDto { NodeId = content.Id, Published = published, Data = serialized.StringData, RawData = serialized.ByteData }; return(dto); }
public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model, bool published) { Compress(content, model, published); var bytes = MessagePackSerializer.Serialize(model, _options); return(new ContentCacheDataSerializationResult(null, bytes)); }