Defining Strongly Typed Topic Models (Java)

Define your own Strongly Typed Topic Models for Tridion Docs Topics and map properties to XHTML elements uses semantic mapping annotations. When mapping properties to elements, you can flatten the DITA structure or use various approaches to define an embedded model.

Using annotations to map properties to XHTML elements

DXA uses the following annotations to map a Strongly Typed Topic Model's properties to XHTML elements:

  • SemanticEntity
  • SemanticProperty

A semantic vocabulary called DitaVocabulary (http://www.sdl.com/web/schemas/dita provides the mapping of the annotations to names of DITA XML elements (encoded as CSS class names in the DITA Open Toolkit XHTML).

Example TopicBody XHTML

In the following XHTML example, you can identify the topic title from the class containing the name "title". The content of this element is the title of the topic. Likewise other specific pieces of information can be identified and extracted from the XHTML. 

<h1 class="title topictitle1 " id="GUID-4A75B581-0D81-412A-A5E1-5E601A7E6DDF__GUID-2D3CF861-276F-4871-9BE0-68B0669A4953">Types of penguins</h1>
<div class="body learningBasebody learningOverviewbody ">
    <div class="section lcIntro " id="GUID-4A75B581-0D81-412A-A5E1-5E601A7E6DDF__LCINTRO_130B2AADEA86480D918C112D4EA3CDC1 ">
        <p class="p ">Nowadays 17 species of penguins exist, subdivided in 6 groups or genus (genera) </p>
        <p class="p ">Some scientists mention the white-flippered little penguin as an 18th species, but most of them consider it as 
         a subspecies of the little penguin. So, we will not adress that one. </p>
    </div>
    <div class="section lcObjectives " id="GUID-4A75B581-0D81-412A-A5E1-5E601A7E6DDF__LCOBJECTIVES_253452FF6D334E78AE47820C20A89B9F "> 
    <span class="ph lcObjectivesStem ">The following topics describe the different species per genus.</span> </div>
</div>
<div class="related-links ">
    <ul class="ullinks ">
        <li class="link ulchildlink "><strong><a href="/2778701/1184170/brush-tailed-penguins.jpg" class="link">Brush-tailed penguins 
         (Pygoscelis)</a></strong>
            <br/> </li>
        <li class="link ulchildlink "><strong><a href="/2778701/1184177/banded-penguins.jpg" class="link">Banded penguins (Spheniscus)</a></strong>
            <br/> </li>
    </ul>
</div>

Java example of a Strongly Typed Topic Model

After the DefaultModelBuilder creates Generic Topic entity models, the StronglyTypedTopicBuilder transforms them into strongly-typed topic models.

The following illustrates one possible model, as implemented in Java:

@SemanticEntity(vocabulary = SemanticVocabulary.SDL_DITA, entityName = "body")
public class LearningBaseTopic extends AbstractEntityModel {
    @SemanticProperty("title")
    private String title;

    @SemanticProperty("lcIntro")
    private RichText intro;

    @SemanticProperty("lcObjectives")
    private RichText objectives;

    @SemanticProperty("related-links/ulchildlink")
    private List<Link> childLinks;

    public String getTitle() {
        return title;
    }

    public RichText getIntro() {
        return intro;
    }

    public RichText getObjectives() {
        return objectives;
    }

    public List<Link> getChildLinks() {
        return childLinks;
    }
}

The semantic mapping annotations names, such as EntityName, correspond to DITA XML element names, which are encoded as CSS class names in the DITA Open Toolkit's XHTML.

Take for example the following mapping:

@SemanticProperty("title")
private String title;
...
public String getTitle() {
        return title;
    }

The [SemanticProperty("title")] is transformed into an XPath that is then used to locate the XML element within the topic body, such as the following:

.//*[contains(@class, 'title')]

Handling embedded elements

DITA structure can be flattened in the View Model. For example, a property Intro maps to DITA element lcIntro. This element is embedded within the body element, but you do not need to be explicitly specify that it is embedded.

On the other hand, you can imply a kind of path syntax in your semantic property names. Take the following example:

@SemanticProperty("related-links/ulchildlink")
private List<Link> childLinks;
...
public List<Link> getChildLinks() {
        return childLinks;
    }

This means that the property should be mapped to DITA XML element ulchildlink within related-links.

This translates to the following XPath:

.//*[contains(@class, 'related-links')]//*[contains(@class, 'ulchildlink')]

Furthermore, you can map DITA elements to embedded Entity Models.

Java example with an embedded Entity Model

In the following example, the entire DITA body is mapped to an embedded Entity Model rather than flattening the DITA body structure.

@SemanticEntity(vocabulary = SemanticVocabulary.SDL_DITA, entityName = "body")
public class LearningBaseTopic extends AbstractEntityModel {
        @SemanticProperty("title")
        private String title;

        @SemanticProperty("body")
        private LearningBaseBody body;

        @SemanticProperty("related-links/ulchildlink")
        private List<Link> childLinks;

        public String getTitle() {
                return title;
        }

        public LearningBaseBody getBody() {
                return body;
        }

        public List<Link> getChildLinks() {
                return childLinks;
        }
};


@SemanticEntity(vocabulary = SemanticVocabulary.SDL_DITA)
public class LearningBaseBody extends AbstractEntityModel {
        @SemanticProperty("lcIntro")
        private RichText intro;

        @SemanticProperty("lcObjectives")
        private RichText objectives;

        public RichText getIntro() {
                return intro;
        }

        public RichText getObjectives() {
                return objectives;
        }
};

The following shows an alternative way to map the same DITA structure that uses a blended approach:

@SemanticEntity(vocabulary = SemanticVocabulary.SDL_DITA, entityName = "body")
public class LearningBaseTopic extends AbstractEntityModel {
        @SemanticProperty("title")
        private String title;

        @SemanticProperty("section")
        private List<Section> sections;

        @SemanticProperty("related-links/ulchildlink")
        private List<Link> childLinks;

        public String getTitle() {
                return title;
        }

        public List<Link> getChildLinks() {
                return childLinks;
        }

        public List<Section> getSections() {
                return sections;
        }
};


@SemanticEntity(vocabulary = SemanticVocabulary.SDL_DITA)
public class LearningBaseBody extends AbstractEntityModel {

        @SemanticProperty("_self")
        private RichText content;

        public RichText getContent() {
                return content;
        }
};

In this example, you see that flattening is used (DITA section elements are within a body element) in combination with mapping to an embedded Entity Model.

This example also demonstrates a special semantic mapping construct: the _self property, which makes it possible to map a property to the same DITA element as the embedded Entity Model represents itself.

Finally, if using embedded Entity Models, you may want to have access to the id or class attribute in the HTML. This is possible through implicit mapping where:

  • The id attribute is mapped to the EntityModel.Id property
  • This class attribute is mapped to the EntityModel.HtmlClasses property