/*
* Copyright 2014 Bazaarvoice, Inc.
*
* Licensed 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 com.bazaarvoice.jolt.jsonUtil.testdomain.two;
import com.bazaarvoice.jolt.Diffy;
import com.bazaarvoice.jolt.JsonUtil;
import com.bazaarvoice.jolt.JsonUtils;
import com.bazaarvoice.jolt.jsonUtil.testdomain.QueryFilter;
import com.bazaarvoice.jolt.jsonUtil.testdomain.QueryParam;
import com.bazaarvoice.jolt.jsonUtil.testdomain.RealFilter;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.testng.AssertJUnit;
import org.testng.annotations.Test;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class MappingTest2 {
private Diffy diffy = new Diffy();
public static class QueryFilterDeserializer extends JsonDeserializer<QueryFilter> {
/**
* Demonstrates how to do recursive polymorphic JSON deserialization in Jackson 2.2.
*
* Aka specify a Deserializer and "catch" some input, determine what type of Class it
* should be parsed too, and then reuse the Jackson infrastructure to recursively do so.
*/
@Override
public QueryFilter deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException {
ObjectCodec objectCodec = jp.getCodec();
ObjectNode root = jp.readValueAsTree();
// Check if it is a "RealFilter"
JsonNode queryParam = root.get("queryParam");
if ( queryParam != null && queryParam.isValueNode() ) {
// pass in our objectCodec so that the subJsonParser knows about our configured Modules and Annotations
JsonParser subJsonParser = root.traverse( objectCodec );
return subJsonParser.readValueAs( RealFilter.class );
}
// We assume it is a LogicalFilter
Iterator<String> iter = root.fieldNames();
String key = iter.next();
JsonNode arrayNode = root.iterator().next();
if ( arrayNode == null || arrayNode.isMissingNode() || ! arrayNode.isArray() ) {
throw new RuntimeException( "Invalid format of LogicalFilter encountered." );
}
// pass in our objectCodec so that the subJsonParser knows about our configured Modules and Annotations
JsonParser subJsonParser = arrayNode.traverse( objectCodec );
List<QueryFilter> childrenQueryFilters = subJsonParser.readValueAs( new TypeReference<List<QueryFilter>>() {} );
return new LogicalFilter2( QueryParam.valueOf( key ), childrenQueryFilters );
}
}
public static class LogicalFilter2Serializer extends JsonSerializer<LogicalFilter2> {
@Override
public void serialize(LogicalFilter2 filter, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeStartObject();
jgen.writeObjectField( filter.getQueryParam().toString(), filter.getFilters().values() );
jgen.writeEndObject();
}
}
@Test
public void testPolymorphicJacksonSerializationAndDeserialization()
{
ObjectMapper mapper = new ObjectMapper();
SimpleModule testModule = new SimpleModule("testModule", new Version(1, 0, 0, null, null, null))
.addDeserializer( QueryFilter.class, new QueryFilterDeserializer() )
.addSerializer( LogicalFilter2.class, new LogicalFilter2Serializer() );
mapper.registerModule(testModule);
// Verifying that we can pass in a custom Mapper and create a new JsonUtil
JsonUtil jsonUtil = JsonUtils.customJsonUtil( mapper );
String testFixture = "/jsonUtils/testdomain/two/queryFilter-realAndLogical2.json";
// TEST JsonUtil and our deserialization logic
QueryFilter queryFilter = jsonUtil.classpathToType( testFixture, new TypeReference<QueryFilter>() {} );
// Make sure the hydrated QFilter looks right
AssertJUnit.assertTrue( queryFilter instanceof LogicalFilter2 );
AssertJUnit.assertEquals( QueryParam.AND, queryFilter.getQueryParam() );
AssertJUnit.assertTrue( queryFilter.isLogical() );
AssertJUnit.assertEquals( 3, queryFilter.getFilters().size() );
AssertJUnit.assertNotNull( queryFilter.getFilters().get( QueryParam.OR ) );
// Make sure one of the top level RealFilters looks right
QueryFilter productIdFilter = queryFilter.getFilters().get( QueryParam.PRODUCTID );
AssertJUnit.assertTrue( productIdFilter.isReal() );
AssertJUnit.assertEquals( QueryParam.PRODUCTID, productIdFilter.getQueryParam() );
AssertJUnit.assertEquals( "Acme-1234", productIdFilter.getValue() );
// Make sure the nested OR looks right
QueryFilter orFilter = queryFilter.getFilters().get( QueryParam.OR );
AssertJUnit.assertTrue( orFilter.isLogical() );
AssertJUnit.assertEquals( QueryParam.OR, orFilter.getQueryParam() );
AssertJUnit.assertEquals( 2, orFilter.getFilters().size() );
// Make sure nested AND looks right
QueryFilter nestedAndFilter = orFilter.getFilters().get( QueryParam.AND );
AssertJUnit.assertTrue( nestedAndFilter.isLogical() );
AssertJUnit.assertEquals( QueryParam.AND, nestedAndFilter.getQueryParam() );
AssertJUnit.assertEquals( 2, nestedAndFilter.getFilters().size() );
// SERIALIZE TO STRING to test serialization logic
String unitTestString = jsonUtil.toJsonString( queryFilter );
// LOAD and Diffy the plain vanilla JSON versions of the documents
Map<String, Object> actual = JsonUtils.jsonToMap( unitTestString );
Map<String, Object> expected = JsonUtils.classpathToMap( testFixture );
// Diffy the vanilla versions
Diffy.Result result = diffy.diff( expected, actual );
if (!result.isEmpty()) {
AssertJUnit.fail( "Failed.\nhere is a diff:\nexpected: " + JsonUtils.toJsonString( result.expected ) + "\n actual: " + JsonUtils.toJsonString( result.actual ) );
}
}
}