DXA Model Mapping Cheatsheet

I recently started working on a new project using the DXA. One of the site types was a rebuild; using an existing content model rather than creating schemas from scratch. Part of the DXA philosophy is to enable MVC developers to use simple view models which hide the complexity of the underlying domain (CM) model. This article shows some tricks you can use to do help take this approach. Its based on the .NET implementation but the same concepts should apply to the Java version.

As we were creating the View Models, rather than trying to mirror 1-1 the rather convoluted existing CMS content model we took the approach to think just about what data should be rendered in that view and to build the view model to make that as simple as possible. Not only does this make for cleaner views, it will give a slight performance improvement, as only properties that you really need are mapped from the domain model. Here are some of the techniques we used:

1. Flattening

Every single Rich Text field, was wrapped up in a generic Rich Text embedded schema. While this makes sense in the domain (CM) model – we want to define common formatting options for all schemas which have fields which allow rich text content, it is a level with no purpose in the View Models. Rather than creating a single RichTextField model, with a single Content property, we can use the technique of flattening to ignore this level in the View models. So while our Promo Box schema might have a body field which uses the embedded Rich Text schema containing a content field, our PromoBox model looks something like this:


public class PromoBox : EntityModel
{
public string Heading { get; set; }
[SemanticProperty("content")]
public RichText Description { get; set; }
}

DXA sees that Description maps to a field called content, and if it can’t find it in the first level of fields, searches for it in embedded fields.

2. Cherry picking

Our ArticleSummary model has a Description property. The business logic was to map this from the Article schema’s first embedded paragraph value’s body field (which by the way uses our embedded Rich Text schema containing a content field). There is no need to define a List Paragraphs property on the ArticleSummary model – we are only interested in the first value, and only the body field’s embedded content value in this. The following will enable us to cherry pick that item:


public class ArticleSummary : EntityModel
{
public string Heading { get; set; }
[SemanticProperty("content")]
public RichText Description { get; set; }
}

DXA actually processes this in the same way as flattening, and even though the field is multivalue, it sees that the property we are mapping is not a List, so just takes the first value. This means that we do not have put clumsy logic in the view to pull out the first paragraph field’s body content – the business logic is encapsulated in the View Model definition

By the way, if you are worried about XPM markup being lost in translation, fear not – DXA has you covered. Checking the markup generated for this field using the Html.DxaPropertyMarkup helper, it reveals that content structure in full:


!-- Start Component Field: {"XPath":"tcm:Content/custom:Article/custom:paragraph[1]/custom:body[1]/custom:paragraph_text[1]/custom:content[1]"} --

3. Merging

Almost every implementation I have worked on has an embedded Link schema, which has the option to select either an internal (Component) or external link, with separate fields for each. In our views, however, we do not care if its an internal or external link, we just want the URL to render. With DXA this is easy – we can map both fields to the same property:


public class PromoBox : EntityModel
{
public string Heading { get; set; }
[SemanticProperty("internalLink")]
[SemanticProperty("externalLink")]
public String Url {get;set;}
}

This example is actually straight from the docs (link) but its good to mention it again, as its very useful that DXA will automatically resolve the internal link to a URL, and prevents the need for clumsy if/else logic in your model or view to test which field has a value.

4. Self-linking

Again – this is mentioned in the docs, but its worth re-iterating. If you like the idea of creating different view models for the same schema, to cater for a Summary / Details type views then you will need to use the self linking concept. Your Details model may not need a link to the item that you are rendering (as presumably you are already on that page), however your Summary model will need a resolved link to the details page. This is done as follows:


public class ArticleSummary : EntityModel
{
public string Heading { get; set; }
[SemanticProperty("content")]
public RichText Description { get; set; }
[SemanticProperty("_self")]
public String Url { get; set; }
}

The _self reserved property name will ensure that a link is resolved to the item itself.

5. Defaults

Last but not least, don’t forget that DXA does default mapping. There is no need to put any SemanticProperty decorators on your View Model properties if the property name matches the schema field XML name (with the first letter small case, so Heading property maps to heading schema XML field name).

I have also noticed that there is a tendency to add SemanticEntity decorators to every EntityModel class definition – its not clear in the docs but you DO NOT NEED THIS. DXA will map whatever component it has to the View Model used by the view it is rendering, it only cares about SemanticEntity decorators if you need to map multiple schemas with differing field XML names, to the same view model (this is the example you see in the docs with the Teaser model), or if you want to use public semantics in your rendered output (schema.org etc.)

6. Ignorance is performance

If you have additional properties in your view model which are not populated from component fields (but rather from an external system or result of some calcuation in the controller/model) then don’t forget to tell DXA to ignore them when mapping content with IgnoreMapping option.


[SemanticProperty(IgnoreMapping = true)]
public int ResultCount { get; set; }

If you don’t do this, DXA will dig around in the source component trying to find some matching field (using the flattening technique described earlier) – this is all wasted effort so using this option will give you a small performance benefit. If you have a lot of ignored properties then you can set MapAllProperties to false on the SemanticEntity decorator – in this way it will only try to map properties with a SemanticProperty decorator


[SemanticDefaults(MapAllProperties = false)]
public class Product : EntityModel
{
//Product Identifier comes from stub component in CMS
[SemanticProperty("sku")]
public String ProductSku {get;set;}
//All other properties come from external PIM and eCom system
public String Title {get;set;}
public String Description {get;set;}
public MediaItem Image {get;set;}
public Price ProductPrice {get;set;}
}

3 thoughts on “DXA Model Mapping Cheatsheet

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>