blob: 1057654e0910807324a44b2315115b2a1a6ba82a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.unomi.persistence.spi;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonTokenId;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.util.*;
/**
* This Jackson deserializer makes it possible to register field matching
* regular expressions that can be matched to class names, as in the following
* example:
*
* SimpleModule deserializerModule =
* new SimpleModule("PropertyTypedObjectDeserializerModule",
* new Version(1, 0, 0, null, "org.apache.unomi.rest", "deserializer"));
* PropertyTypedObjectDeserializer propertyTypedObjectDeserializer = new PropertyTypedObjectDeserializer();
* propertyTypedObjectDeserializer.registerMapping("type=.*Condition", Condition.class);
* deserializerModule.addDeserializer(Object.class, propertyTypedObjectDeserializer);
* objectMapper.registerModule(deserializerModule);
*
* In this example any JSON object that has a "type" property that matches the
* ".*Condition" regular expression will be parsed and mapped to a Condition class
*
* Note that there exists a way to map properties as type identifiers in Jackson,
* but this feature is very limited and requires hardcoding possible values.
* This deserializer is much more flexible and powerful.
*/
public class PropertyTypedObjectDeserializer extends UntypedObjectDeserializer {
private static final long serialVersionUID = -2561171359946902967L;
private Map<String, Class<? extends Object>> registry =
new LinkedHashMap<String, Class<? extends Object>>();
private Map<String,Set<String>> fieldValuesToMatch = new LinkedHashMap<String,Set<String>>();
public void registerMapping(String matchExpression,
Class<? extends Object> mappedClass) {
registry.put(matchExpression, mappedClass);
String[] fieldParts = matchExpression.split("=");
Set<String> valuesToMatch = fieldValuesToMatch.get(fieldParts[0]);
if (valuesToMatch == null) {
valuesToMatch = new LinkedHashSet<String>();
}
valuesToMatch.add(fieldParts[1]);
fieldValuesToMatch.put(fieldParts[0], valuesToMatch);
}
@Override
public Object deserialize(
JsonParser jp, DeserializationContext ctxt)
throws IOException {
if (jp.getCurrentTokenId() != JsonTokenId.ID_START_OBJECT) {
return super.deserialize(jp, ctxt);
}
ObjectCodec codec = jp.getCodec();
TreeNode treeNode = codec.readTree(jp);
Class<? extends Object> objectClass = null;
if (treeNode instanceof ObjectNode) {
ObjectNode root = (ObjectNode) treeNode;
Iterator<Map.Entry<String, JsonNode>> elementsIterator =
root.fields();
while (elementsIterator.hasNext()) {
Map.Entry<String, JsonNode> element = elementsIterator.next();
String name = element.getKey();
if (fieldValuesToMatch.containsKey(name)) {
Set<String> valuesToMatch = fieldValuesToMatch.get(name);
for (String valueToMatch : valuesToMatch) {
if (element.getValue().asText().matches(valueToMatch)) {
objectClass = registry.get(name + "=" + valueToMatch);
break;
}
}
if (objectClass != null) {
break;
}
}
}
if (objectClass == null) {
objectClass = HashMap.class;
}
} else {
}
if (objectClass == null) {
return super.deserialize(codec.treeAsTokens(treeNode), ctxt);
}
return codec.treeToValue(treeNode, objectClass);
}
}