Created
July 2, 2018 20:42
-
-
Save bmcdavid/3dda37992dd5130436392b67b2b9b7cb to your computer and use it in GitHub Desktop.
Vulcan Episerver ContentRefernce Converter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Serializer for Vulcan content | |
/// </summary> | |
// ReSharper disable once InconsistentNaming | |
public class CustomJsonSerializer : JsonNetSerializer | |
private readonly IEnumerable<IVulcanIndexingModifier> _vulcanModifiers; | |
private static readonly Type IgnoreType = typeof(VulcanIgnoreAttribute); | |
protected override IList<Func<Type, JsonConverter>> ContractConverters { get; } = new List<Func<Type, JsonConverter>> | |
{ | |
checkType => ContentRefType.IsAssignableFrom(checkType) ? new VulcanContentReferenceConverter() : null | |
}; | |
private static readonly Type ContentRefType = typeof(ContentReference); | |
/// <summary> | |
/// Ignored property mapping types | |
/// </summary> | |
protected static Type[] IgnoredPropertyTypes = | |
{ | |
typeof(PropertyDataCollection), | |
typeof(ContentArea), | |
typeof(CultureInfo), | |
typeof(IEnumerable<CultureInfo>), | |
typeof(EPiServer.DataAbstraction.PageType), | |
typeof(EPiServer.Framework.Blobs.Blob) | |
}; | |
/// <summary> | |
/// DI Constructor | |
/// </summary> | |
/// <param name="settings"></param> | |
/// <param name="modifiers"></param> | |
/// <param name="settingsModifier"></param> | |
public WSOLCustomJsonSerializer | |
( | |
IConnectionSettingsValues settings, | |
IEnumerable<IVulcanIndexingModifier> modifiers, | |
Action<JsonSerializerSettings, IConnectionSettingsValues> settingsModifier | |
) : base(settings, settingsModifier) | |
{ | |
_vulcanModifiers = modifiers; | |
} | |
/// <summary> | |
/// Creates property mapping | |
/// </summary> | |
/// <param name="memberInfo"></param> | |
/// <returns></returns> | |
public override IPropertyMapping CreatePropertyMapping(MemberInfo memberInfo) | |
{ | |
var propertyType = (memberInfo as PropertyInfo)?.PropertyType; | |
// have to use Attribute.GetCustomAttributes, if types are proxied checking memberinfo directly is always false | |
var isIgnored = Attribute.GetCustomAttributes(memberInfo, IgnoreType, true).Length > 0; | |
if | |
( | |
isIgnored || | |
memberInfo.Name.Equals("PageName", StringComparison.OrdinalIgnoreCase) || | |
memberInfo.Name.Contains(".") || | |
memberInfo.MemberType == MemberTypes.Property && | |
( | |
IsSubclassOfRawGeneric(typeof(Injected<>), propertyType) || | |
IgnoredPropertyTypes.Contains(propertyType) || | |
memberInfo.Name.Equals("DefaultMvcController", StringComparison.OrdinalIgnoreCase)) | |
) | |
{ | |
return new PropertyMapping { Ignore = true }; | |
} | |
return base.CreatePropertyMapping(memberInfo); | |
} | |
/// <summary> | |
/// Serialize data | |
/// </summary> | |
/// <param name="data"></param> | |
/// <param name="writableStream"></param> | |
/// <param name="formatting"></param> | |
public override void Serialize(object data, Stream writableStream, SerializationFormatting formatting = SerializationFormatting.Indented) | |
{ | |
// ReSharper disable once MergeCastWithTypeCheck | |
if (data is IndexDescriptor<IContent> descriptedData) | |
{ | |
// write all but ending } | |
CopyDataToStream(data, writableStream, formatting); | |
var content = ((IIndexRequest<IContent>)descriptedData).Document; | |
if (content != null && _vulcanModifiers != null) | |
{ | |
// try to inspect to see if a pipeline was enabled | |
var requestAccessor = (IRequest<IndexRequestParameters>)data; | |
var pipelineId = requestAccessor.RequestParameters?.GetQueryStringValue<string>("pipeline"); // returns null if key not found | |
var args = new VulcanIndexingModifierArgs(content, pipelineId); | |
foreach (var indexingModifier in _vulcanModifiers) | |
{ | |
try | |
{ | |
indexingModifier.ProcessContent(args); | |
} | |
catch (Exception e) | |
{ | |
throw new Exception($"{indexingModifier.GetType().FullName} failed to process content ID {content.ContentLink.ID} with name {content.Name}!", e); | |
} | |
} | |
// add separator for additional items if any | |
if (args.AdditionalItems.Any()) | |
{ | |
WriteToStream(" , ", writableStream); | |
} | |
// copy all but starting { | |
CopyDataToStream(args.AdditionalItems, writableStream, formatting, false); | |
} | |
else | |
{ | |
WriteToStream("}", writableStream); // add back closing | |
} | |
} | |
else | |
{ | |
base.Serialize(data, writableStream, formatting); | |
} | |
} | |
private static bool IsSubclassOfRawGeneric(Type generic, Type toCheck) | |
{ | |
while (toCheck != null && toCheck != typeof(object)) | |
{ | |
var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck; | |
if (generic == cur) | |
{ | |
return true; | |
} | |
toCheck = toCheck.BaseType; | |
} | |
return false; | |
} | |
private static void WriteToStream(string data, Stream writeableStream) | |
{ | |
var streamWriter = new StreamWriter(writeableStream); | |
streamWriter.Write(data); | |
streamWriter.Flush(); | |
} | |
//copies all but first or last byte | |
private void CopyDataToStream(object data, Stream writableStream, SerializationFormatting formatting, bool trimLast = true) | |
{ | |
var stream = new MemoryStream(); | |
base.Serialize(data, stream, formatting); | |
stream.Seek(trimLast ? 0 : 1, SeekOrigin.Begin); | |
var bytes = Convert.ToInt32(stream.Length); | |
var buffer = new byte[32768]; | |
int read; | |
if (trimLast) | |
bytes--; | |
while (bytes > 0 && | |
(read = stream.Read(buffer, 0, Math.Min(buffer.Length, bytes))) > 0) | |
{ | |
writableStream.Write(buffer, 0, read); | |
bytes -= read; | |
} | |
stream.Flush(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class VulcanContentReferenceConverter : JsonConverter | |
{ | |
private static readonly Type ContentReferenceType = typeof(ContentReference); | |
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | |
{ | |
var contentRef = value as ContentReference; | |
if (contentRef == null) | |
writer.WriteNull(); | |
else | |
writer.WriteValue(contentRef.ToReferenceWithoutVersion().ToString()); | |
} | |
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |
{ | |
if (reader.TokenType == JsonToken.Null) | |
{ | |
return null; | |
} | |
if (reader.TokenType == JsonToken.String) | |
{ | |
return new ContentReference((string)reader.Value); | |
} | |
if (reader.TokenType == JsonToken.Integer) | |
{ | |
return new ContentReference((int)reader.Value); | |
} | |
throw new JsonSerializationException($"Cannot convert token of type {reader.TokenType} to {objectType}."); | |
} | |
public override bool CanConvert(Type objectType) | |
{ | |
return ContentReferenceType.IsAssignableFrom(objectType); | |
} | |
public override bool CanRead { get; } = false; | |
public override bool CanWrite { get; } = true; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment