001package com.identityworksllc.iiq.common; 002 003import com.fasterxml.jackson.annotation.JsonAutoDetect; 004import com.fasterxml.jackson.annotation.JsonFilter; 005import com.fasterxml.jackson.annotation.JsonInclude; 006import com.fasterxml.jackson.annotation.PropertyAccessor; 007import com.fasterxml.jackson.databind.ObjectMapper; 008import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; 009import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; 010import com.fasterxml.jackson.databind.type.MapType; 011import sailpoint.tools.GeneralException; 012 013import java.util.Collections; 014import java.util.HashMap; 015import java.util.Map; 016import java.util.Set; 017 018/** 019 * An interface implementing a {@link #toMap()} default method to transform 020 * any object into a Map using Jackson. Simply implement this interface and 021 * enjoy the use of toMap() without any boilerplate code. 022 */ 023public interface Mappable { 024 025 /** 026 * Sneaky mixin to enable this class to use its custom filter on classes 027 * that don't support it 028 */ 029 @JsonFilter("mappableFilter") 030 @JsonInclude(JsonInclude.Include.NON_NULL) 031 final class FilterMixin { 032 // Deliberately empty because this is only used for the annotations 033 } 034 035 /** 036 * Returns a non-null Map representation of the given object using 037 * Jackson as a transformation engine. 038 * 039 * If the input is null, an empty HashMap will be returned. 040 * 041 * @param whatever The object to transform 042 * @param exclusions A set of fields to exclude from serialization, or null if none 043 * @return The resulting map 044 */ 045 static Map<String, Object> toMap(Object whatever, Set<String> exclusions) { 046 if (whatever == null) { 047 return new HashMap<>(); 048 } 049 050 ObjectMapper mapper = new ObjectMapper(); 051 mapper.addMixIn(whatever.getClass(), FilterMixin.class); 052 mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); 053 SimpleFilterProvider filterProvider; 054 if (exclusions != null && !exclusions.isEmpty()) { 055 filterProvider = new SimpleFilterProvider().addFilter("mappableFilter", SimpleBeanPropertyFilter.serializeAllExcept(exclusions)); 056 } else { 057 filterProvider = new SimpleFilterProvider().addFilter("mappableFilter", SimpleBeanPropertyFilter.serializeAll()); 058 } 059 mapper.setFilterProvider(filterProvider); 060 MapType javaType = mapper.getTypeFactory().constructMapType(HashMap.class, String.class, Object.class); 061 return mapper.convertValue(whatever, javaType); 062 } 063 064 /** 065 * Returns a non-null Map representation of this object. 066 * 067 * Only non-null values will be included by default. 068 * 069 * @return A Map representation of this object. 070 * @throws GeneralException if Map conversion fails for any reason 071 */ 072 default Map<String, Object> toMap() throws GeneralException { 073 return Mappable.toMap(this, toMapFieldExclusions()); 074 } 075 076 /** 077 * Optionally returns a list of fields to exclude from serialization. This 078 * will be used in addition to any fields annotated with JsonIgnore. 079 * 080 * @return Returns a list of fields to exclude from serialization 081 */ 082 default Set<String> toMapFieldExclusions() { 083 return Collections.emptySet(); 084 } 085}