io.katharsis.jackson.serializer.ContainerSerializer Maven / Gradle / Ivy
package io.katharsis.jackson.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.PropertyWriter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import io.katharsis.jackson.exception.JsonSerializationException;
import io.katharsis.queryParams.params.IncludedFieldsParams;
import io.katharsis.queryParams.params.IncludedRelationsParams;
import io.katharsis.queryParams.params.TypedParams;
import io.katharsis.request.dto.Attributes;
import io.katharsis.resource.field.ResourceField;
import io.katharsis.resource.information.ResourceInformation;
import io.katharsis.resource.registry.RegistryEntry;
import io.katharsis.resource.registry.ResourceRegistry;
import io.katharsis.response.Container;
import io.katharsis.response.ContainerType;
import io.katharsis.response.DataLinksContainer;
import io.katharsis.utils.BeanUtils;
import io.katharsis.utils.Predicate2;
import io.katharsis.utils.PropertyUtils;
import io.katharsis.utils.java.Optional;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* This class serializes an single resource which can be included in data field of JSON API response.
*
* @see Container
*/
public class ContainerSerializer extends JsonSerializer {
private static final String TYPE_FIELD_NAME = "type";
private static final String ID_FIELD_NAME = "id";
private static final String ATTRIBUTES_FIELD_NAME = "attributes";
private static final String RELATIONSHIPS_FIELD_NAME = "relationships";
private static final String LINKS_FIELD_NAME = "links";
private static final String META_FIELD_NAME = "meta";
private static final String SELF_FIELD_NAME = "self";
private static final String JACKSON_ATTRIBUTE_FILTER_NAME = "katharsisFilter";
private final ResourceRegistry resourceRegistry;
public ContainerSerializer(ResourceRegistry resourceRegistry) {
this.resourceRegistry = resourceRegistry;
}
@Override
public void serialize(Container container, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (container != null && container.getData() != null) {
gen.writeStartObject();
TypedParams includedFields = null;
IncludedRelationsParams includedRelationsParams = null;
if (container.getResponse().getQueryParams() != null) {
includedFields = container.getResponse()
.getQueryParams()
.getIncludedFields();
TypedParams includedRelations = container.getResponse()
.getQueryParams()
.getIncludedRelations();
Class> dataClass = container.getData().getClass();
String resourceType = resourceRegistry.getResourceType(dataClass);
if (includedRelations != null &&
includedRelations.getParams().containsKey(resourceType)) {
includedRelationsParams = includedRelations.getParams().get(resourceType);
} else if (includedRelations != null &&
container.getContainerType() != null &&
container.getContainerType().equals(ContainerType.INCLUDED)) {
for (IncludedRelationsParams includedRelationsParamsInner : includedRelations.getParams().values()) {
if (includedRelationsParamsInner.getParams().iterator().next().getPathList().get(0).equals(container.getIncludedFieldName())) {
includedRelationsParams = includedRelationsParamsInner;
}
}
}
}
writeData(container, gen, container.getData(), includedFields, includedRelationsParams);
gen.writeEndObject();
} else {
gen.writeObject(null);
}
}
/**
* Writes a value. Each serialized container must contain type field whose value is string
* .
*/
private void writeData(Container container, JsonGenerator gen, Object data, TypedParams includedFields,
IncludedRelationsParams includedRelations) throws IOException {
Class> dataClass = data.getClass();
String resourceType = resourceRegistry.getResourceType(dataClass);
gen.writeStringField(TYPE_FIELD_NAME, resourceType);
RegistryEntry entry = resourceRegistry.getEntry(dataClass);
ResourceInformation resourceInformation = entry.getResourceInformation();
try {
writeId(gen, data, resourceInformation.getIdField());
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new JsonSerializationException(
"Error writing id field: " + resourceInformation.getIdField().getUnderlyingName());
}
Set notAttributesFields = entry.getResourceInformation().getNotAttributeFields();
writeAttributes(gen, data, includedFields, notAttributesFields);
Set relationshipFields = getRelationshipFields(resourceType, resourceInformation, includedFields);
if (!relationshipFields.isEmpty()) {
writeRelationshipFields(container, gen, data, relationshipFields, includedRelations);
}
writeMetaField(gen, data, entry);
writeLinksField(gen, data, entry);
}
private Set getRelationshipFields(String resourceType, ResourceInformation resourceInformation,
TypedParams includedFields) {
Set relationshipFields = new HashSet<>();
Optional> fields = includedFields(resourceType, includedFields);
if (fields.isPresent()) {
for (ResourceField resourceField : resourceInformation.getRelationshipFields()) {
if (fields.get().contains(resourceField.getJsonName())) {
relationshipFields.add(resourceField);
}
}
} else {
relationshipFields.addAll(resourceInformation.getRelationshipFields());
}
return relationshipFields;
}
/**
* The id MUST be written as a string
* Resource IDs.
*/
private static void writeId(JsonGenerator gen, Object data, ResourceField idField)
throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, IOException {
String sourceId = BeanUtils.getProperty(data, idField.getUnderlyingName());
gen.writeObjectField(ID_FIELD_NAME, sourceId);
}
/**
* Writes resource attributes object taking into account fields query params. It doesn't allow writing
* null resource attributes.
*
* @param gen Jackson generator
* @param data resource object
* @param includedFields field query param values
* @param notAttributesFields names of relationships and id field
* @throws IOException if couldn't write attributes
*/
private void writeAttributes(JsonGenerator gen, final Object data, TypedParams includedFields,
final Set notAttributesFields)
throws IOException {
String resourceType = resourceRegistry.getResourceType(data.getClass());
final Optional> fields = includedFields(resourceType, includedFields);
Map dataMap;
if (fields.isPresent()) {
Predicate2
© 2015 - 2024 Weber Informatics LLC | Privacy Policy