Skip to content

Commit

Permalink
TSI-2262 introducing od DXA 2.0 data model and polymorphic JSON parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexey Zarakovskiy committed Feb 1, 2017
1 parent 8ee01d5 commit 7a36a95
Show file tree
Hide file tree
Showing 18 changed files with 829 additions and 4 deletions.
@@ -1,11 +1,13 @@
package com.sdl.dxa;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.fasterxml.jackson.databind.util.StdDateFormat;
import com.fasterxml.jackson.datatype.joda.JodaModule;
import com.sdl.dxa.api.model.data.util.PolymorphicObjectMixin;
import com.sdl.webapp.common.api.contextengine.ContextEngine;
import com.sdl.webapp.common.api.serialization.json.DxaViewModelJsonChainFilter;
import com.sdl.webapp.common.util.ApplicationContextHolder;
Expand Down Expand Up @@ -157,6 +159,8 @@ public ObjectMapper objectMapper() {
objectMapper.setFilterProvider(jsonFilterProvider());
objectMapper.setDateFormat(new StdDateFormat());
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategy.UpperCamelCaseStrategy());
objectMapper.addMixIn(Object.class, PolymorphicObjectMixin.class);
traceBeanInitialization(objectMapper);
return objectMapper;
}
Expand Down
@@ -0,0 +1,17 @@
package com.sdl.dxa.api.model.data;

import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Value;

@Value
@JsonTypeName
public class BinaryContentData {

private String fileName;

private long fileSize;

private String mimeType;

private String url;
}
@@ -0,0 +1,29 @@
package com.sdl.dxa.api.model.data;

import com.fasterxml.jackson.annotation.JsonTypeName;
import org.jetbrains.annotations.NotNull;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

/**
* {@link Map} implemenation to handle DXA polymorphic JSON logic. Is created to be able to address to {@code ContentModelData[]}.
*/
@JsonTypeName
public class ContentModelData extends HashMap<String, Object>
implements Map<String, Object>, Cloneable, Serializable {

/**
* Returns and element from the map and casts it to a given class. Basically calls {@link Map#get(Object)} and casts.
* Throws a {@link ClassCastException} is casting is not successfull.
*
* @param key key of the element
* @param expectedClass class to cast to
* @param <T> a required type
* @return en element if any, null otherwise
*/
public <T> T getAndCast(String key, @NotNull Class<T> expectedClass) {
return expectedClass.cast(get(key));
}
}
@@ -0,0 +1,21 @@
package com.sdl.dxa.api.model.data;

import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.EqualsAndHashCode;
import lombok.Value;

@Value
@EqualsAndHashCode(callSuper = true)
@JsonTypeName
public class EntityModelData extends ViewModelData {

private String id;

private String linkUrl;

private ContentModelData content;

private BinaryContentData binaryContent;

private ExternalContentData externalContent;
}
@@ -0,0 +1,15 @@
package com.sdl.dxa.api.model.data;

import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Value;

@Value
@JsonTypeName
public class ExternalContentData {

private String displayTypeId;

private String id;

private ContentModelData metadata;
}
@@ -0,0 +1,21 @@
package com.sdl.dxa.api.model.data;

import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.EqualsAndHashCode;
import lombok.Value;

@Value
@EqualsAndHashCode(callSuper = true)
@JsonTypeName
public class KeywordModelData extends ViewModelData {

private String id;

private String description;

private String key;

private String taxonomyId;

private String title;
}
@@ -0,0 +1,23 @@
package com.sdl.dxa.api.model.data;

import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Value;

import java.util.Map;

@Value
@JsonTypeName
public class MvcData {

private String actionName;

private String areaName;

private String controllerAreaName;

private String controllerName;

private String viewName;

private Map<String, String> parameters;
}
@@ -0,0 +1,22 @@
package com.sdl.dxa.api.model.data;

import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.EqualsAndHashCode;
import lombok.Value;

import java.util.List;
import java.util.Map;

@Value
@EqualsAndHashCode(callSuper = true)
@JsonTypeName
public class PageModelData extends ViewModelData {

private String id;

private Map<String, String> meta;

private String title;

private List<RegionModelData> regions;
}
@@ -0,0 +1,21 @@
package com.sdl.dxa.api.model.data;

import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.EqualsAndHashCode;
import lombok.Value;

import java.util.List;

@Value
@EqualsAndHashCode(callSuper = true)
@JsonTypeName
public class RegionModelData extends ViewModelData {

private String name;

private String includePageUrl;

private List<EntityModelData> entities;

private List<RegionModelData> regions;
}
@@ -0,0 +1,13 @@
package com.sdl.dxa.api.model.data;

import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Value;

import java.util.List;

@Value
@JsonTypeName
public class RichTextData {

private List<?> fragments;
}
@@ -0,0 +1,35 @@
package com.sdl.dxa.api.model.data;

import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.util.Map;

/**
* Base class of DXA Data Model, the top of View Model hierarchy.
* <p>Introduced as a replacement for the old DD4T data model which is unnecessarily too verbose forcing to do all
* the mapping logic os a server-side that made some sense in its original purpose but is not needed in DXA
* since all the schemas and types are known out of the box.</p>
*/
@ToString
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
@Getter
public abstract class ViewModelData {

private String schemaId;

private String htmlClasses;

private Map<String, ?> xpmMetadata;

private ContentModelData metadata;

private Map<String, ?> extensionData;

private MvcData mvcData;
}
@@ -0,0 +1,104 @@
package com.sdl.dxa.api.model.data.util;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.sdl.dxa.api.model.data.ContentModelData;
import com.sdl.dxa.api.model.data.EntityModelData;
import com.sdl.dxa.api.model.data.KeywordModelData;
import com.sdl.dxa.api.model.data.RichTextData;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.List;

/**
* Wrapper for the polymorphic list JSON representation coming from .NET Template Builder.
* <p>It serializes the list as the following
* <pre><code>
* {
* "list": {
* "$type": "ContentModelData[]",
* "$values": [
* { ... }, { ... }
* ]
* }
* }
* </code></pre>
* meaning that all elements of a list are of type {@code {@link ContentModelData}}</p>
* <p>For the known and excepted types of list subtypes are created so that type information is not expected on leaves.
* In case of using the generic type, type information is added and expected on leaves by deserializer.</p>
* <p>Unfortunately it's currently not possible to implement {@link List} interface since Jackson is handling this differently.
* This is to be investigated and done in the future.</p>
*
* @param <T> type of elements of the list
*/
@Setter(value = AccessLevel.NONE)
@Data
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ListWrapper<T> {

@JsonProperty("$values")
private List<T> values;

public T get(int index) {
return values.get(index);
}

public boolean empty() {
return values.isEmpty();
}

/**
* The concrete implementation of {@link ListWrapper} for {@link ContentModelData}.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@JsonTypeName("ContentModelData[]")
public static class ContentModelDataListWrapper extends ListWrapper<ContentModelData> {

public ContentModelDataListWrapper(List<ContentModelData> values) {
super(values);
}
}


/**
* The concrete implementation of {@link ListWrapper} for {@link ContentModelData}.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@JsonTypeName("KeywordModelData[]")
public static class KeywordModelDataListWrapper extends ListWrapper<KeywordModelData> {

public KeywordModelDataListWrapper(List<KeywordModelData> values) {
super(values);
}
}


/**
* The concrete implementation of {@link ListWrapper} for {@link EntityModelData}.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@JsonTypeName("EntityModelData[]")
public static class EntityModelDataListWrapper extends ListWrapper<EntityModelData> {

public EntityModelDataListWrapper(List<EntityModelData> values) {
super(values);
}
}

/**
* The concrete implementation of {@link ListWrapper} for {@link RichTextData}.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@JsonTypeName("RichTextData[]")
public static class RichTextFragmentListWrapper extends ListWrapper<RichTextData> {

public RichTextFragmentListWrapper(List<RichTextData> values) {
super(values);
}
}
}

0 comments on commit 7a36a95

Please sign in to comment.