Sortr.java
/*
* Copyright 2013 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;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Recursively sorts all maps within a JSON object into new sorted LinkedHashMaps so that serialized
* representations are deterministic. Useful for debugging and making test fixtures.
*
* Note this will make a copy of the input Map and List objects.
*
* The sort order is standard alphabetical ascending, with a special case for "~" prefixed keys to be bumped to the top.
*/
public class Sortr implements Transform {
/**
* Makes a "sorted" copy of the input JSON for human readability.
*
* @param input the JSON object to transform, in plain vanilla Jackson Map<String, Object> style
*/
@Override
public Object transform( Object input ) {
return sortJson( input );
}
@SuppressWarnings( "unchecked" )
public static Object sortJson( Object obj ) {
if ( obj instanceof Map ) {
return sortMap( (Map<String, Object>) obj );
} else if ( obj instanceof List ) {
return ordered( (List<Object>) obj );
} else {
return obj;
}
}
private static Map<String, Object> sortMap( Map<String, Object> map ) {
List<String> keys = new ArrayList<>( map.keySet() );
Collections.sort( keys, jsonKeyComparator );
LinkedHashMap<String,Object> orderedMap = new LinkedHashMap<>( map.size() );
for ( String key : keys ) {
orderedMap.put( key, sortJson( map.get(key) ) );
}
return orderedMap;
}
private static List<Object> ordered( List<Object> list ) {
// Don't sort the list because that would change intent, but sort its components
// Additionally, make a copy of the List in-case the provided list is Immutable / Unmodifiable
List<Object> newList = new ArrayList<>( list.size() );
for ( Object obj : list ) {
newList.add( sortJson( obj ) );
}
return newList;
}
private final static JsonKeyComparator jsonKeyComparator = new JsonKeyComparator();
/**
* Standard alphabetical sort, with a special case for keys beginning with "~".
*/
private static class JsonKeyComparator implements Comparator<String> {
@Override
public int compare(String a, String b) {
boolean aTilde = ( a.length() > 0 && a.charAt(0) == '~' );
boolean bTilde = ( b.length() > 0 && b.charAt(0) == '~' );
if ( aTilde && ! bTilde ) {
return -1;
}
if ( ! aTilde && bTilde ) {
return 1;
}
return a.compareTo( b );
}
}
}