Sitecore Headless – Demystifying item serializers

In my previous posts I showed how you can extend the Layout Service and implement your own custom Contents Resolvers. Until now I simply wanted to serialize all non-standard Sitecore fields with its values. Although there could be a case where you need to include or exclude fields from the Layout Service response.

By default Sitecore provides the following item serializers:

  • DefaultItemSerializer
    • Excludes all the standard Sitecore fields (field names starting with __)
  • AllFieldsItemSerializer
    • As you could expect it includes all fields, including the Sitecore standard fields
  • JssItemSerializer
    • Inherited from the DefaultItemSerializer, but depedning on the Context.PageMode
    • If page mode is IsExperienceEditorEditing then it includes empty fields in the serialization to able to edit in Experience Editor

When you install the Sitecore Headless module it comes with the following configuration which is related to the item serializers:

...
<config name="default">
<rendering type="Sitecore.LayoutService.Configuration.DefaultRenderingConfiguration, Sitecore.LayoutService">
...
<itemSerializer type="Sitecore.LayoutService.Serialization.ItemSerializers.DefaultItemSerializer, Sitecore.LayoutService" resolve="true">
<AlwaysIncludeEmptyFields>true</AlwaysIncludeEmptyFields>
</itemSerializer>
...
</rendering>
</config>
<config name="jss">
<rendering type="Sitecore.LayoutService.Configuration.DefaultRenderingConfiguration, Sitecore.LayoutService">
...
<itemSerializer type="Sitecore.JavaScriptServices.ViewEngine.LayoutService.JssItemSerializer, Sitecore.JavaScriptServices.ViewEngine" resolve="true">
<AlwaysIncludeEmptyFields>true</AlwaysIncludeEmptyFields>
</itemSerializer>
...
</rendering>
</config>
...

In the config above, you can see that the DefaultItemSerializer and JssItemSerializer is used for the Layout Service response. If you would like to overwrite the serialization behavior for all content resolvers you should do it here.

But what if…

I want to use a different serialization only for specific content resolvers?

Then you should do a trick, let me explain it how. The ItemSerializer object is part of the IRenderingConfiguration which is passed as a parameter for all contents resolvers (IRenderingContentsResolver). The only problem is that the IRenderingConfiguration.ItemSerializer is a read only object (not sure why, but this is how it is). So to able inject your custom ItemSerializer you need pick up the original IRenderingConfiguration and create a new object from it, copy all properties except the one which you want to overwrite, in our case the ItemSerializer. Let me show the code to make it clear.

#1 Custom item serializer, in this example it excludes specific fields of the item for serialization. If you don’t need a custom serializer because you can use one of the built in serializers you can skip this step:

public interface ICustomItemSerializer : IItemSerializer
{
}
public class CustomItemSerializer : DefaultItemSerializer, ICustomItemSerializer
{
/// <summary>
/// TemplateId - FieldIds to exclude
/// </summary>
private static readonly IDictionary<ID, ID[]> _fieldsToExclude = new Dictionary<ID, ID[]>()
{
{
new ID(Constants.Template1.TemplateId),
new ID[]
{
Constants.Template1.Fields.Field1,
Constants.Template1.Fields.Field2,
}
}
};
public CustomItemSerializer(IGetFieldSerializerPipeline getFieldSerializerPipeline) : base(getFieldSerializerPipeline)
{
}
protected override IEnumerable<Field> GetItemFields(Item item)
{
if (!_fieldsToExclude.TryGetValue(item.TemplateID, out var fields))
{
return base.GetItemFields(item);
}
return base.GetItemFields(item).Where(f => !fields.Contains(f.ID));
}
}

#2 Rendering configuration factory which can be reused if you have multiple custom item serializers:

public interface IRenderingConfigFactory
{
IRenderingConfiguration Create<T>(IRenderingConfiguration renderingConfig, T itemSerializer) where T : class, IItemSerializer;
}
public class RenderingConfigFactory : IRenderingConfigFactory
{
public IRenderingConfiguration Create<T>(IRenderingConfiguration renderingConfig, T itemSerializer) where T : class, IItemSerializer
{
return new DefaultRenderingConfiguration
{
IncludeServerUrlInContextItemMediaUrls = renderingConfig.IncludeServerUrlInContextItemMediaUrls,
ItemSerializer = itemSerializer, // Custom item serializer
Name = renderingConfig.Name,
PlaceholdersResolver = renderingConfig.PlaceholdersResolver,
RenderingContentsResolver = renderingConfig.RenderingContentsResolver,
};
}
}

#3 Custom contents resolver which is using the RenderingConfigFactory to create a new rendering configuration based on the original config:

public class CustomContentsResolver : RenderingContentsResolver
{
private readonly ICustomItemSerializer _customItemSerializer;
private readonly IRenderingConfigFactory _renderingConfigFactory;
public CustomContentsResolver(
ICustomItemSerializer customItemSerializer,
IRenderingConfigFactory renderingConfigFactory)
{
_customItemSerializer = customItemSerializer;
_renderingConfigFactory = renderingConfigFactory;
}
public override object ResolveContents(Rendering rendering, IRenderingConfiguration renderingConfig)
{
var customRenderingConfig = _renderingConfigFactory.Create(renderingConfig, _customItemSerializer);
return base.ResolveContents(rendering, customRenderingConfig);
}
}

#4 To make it work, the custom dependencies needs to be registered in the Sitecore DI container, but to keep the post short and focused on item serializer implementation I skip that.

Conclusion

This is an implementation of the idea of having unique item serializers for a contents resolver but it can be implemented in a more generic way, e.g. using the contents resolver Sitecore parameters to define which fields to exclude or include, therefore we don’t need to implement a contents resolver for each item serializer.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s