pl.jalokim.propertiestojson.util.PropertiesToJsonConverter Maven / Gradle / Ivy
package pl.jalokim.propertiestojson.util;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import pl.jalokim.propertiestojson.AlgorithmType;
import pl.jalokim.propertiestojson.JsonObjectsTraverseResolver;
import pl.jalokim.propertiestojson.helper.PropertiesWithInsertOrder;
import pl.jalokim.propertiestojson.helper.PropertyKeysOrderResolver;
import pl.jalokim.propertiestojson.object.ObjectJsonType;
import pl.jalokim.propertiestojson.path.PathMetadata;
import pl.jalokim.propertiestojson.path.PathMetadataBuilder;
import pl.jalokim.propertiestojson.resolvers.ArrayJsonTypeResolver;
import pl.jalokim.propertiestojson.resolvers.JsonTypeResolver;
import pl.jalokim.propertiestojson.resolvers.ObjectJsonTypeResolver;
import pl.jalokim.propertiestojson.resolvers.PrimitiveJsonTypesResolver;
import pl.jalokim.propertiestojson.resolvers.primitives.PrimitiveJsonTypeResolver;
import pl.jalokim.propertiestojson.resolvers.primitives.StringJsonTypeResolver;
import pl.jalokim.propertiestojson.resolvers.primitives.adapter.PrimitiveJsonTypeResolverToNewApiAdapter;
import pl.jalokim.propertiestojson.resolvers.primitives.object.NullToJsonTypeConverter;
import pl.jalokim.propertiestojson.resolvers.primitives.object.ObjectToJsonTypeConverter;
import pl.jalokim.propertiestojson.resolvers.primitives.object.StringToJsonTypeConverter;
import pl.jalokim.propertiestojson.resolvers.primitives.string.TextToConcreteObjectResolver;
import pl.jalokim.propertiestojson.resolvers.primitives.string.TextToEmptyStringResolver;
import pl.jalokim.propertiestojson.resolvers.primitives.string.TextToJsonNullReferenceResolver;
import pl.jalokim.propertiestojson.util.exception.ParsePropertiesException;
import pl.jalokim.propertiestojson.util.exception.ReadInputException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import static java.util.Objects.requireNonNull;
import static pl.jalokim.propertiestojson.Constants.ARRAY_START_SIGN;
import static pl.jalokim.propertiestojson.resolvers.primitives.object.NullToJsonTypeConverter.NULL_TO_JSON_RESOLVER;
import static pl.jalokim.propertiestojson.resolvers.primitives.object.StringToJsonTypeConverter.STRING_TO_JSON_RESOLVER;
import static pl.jalokim.propertiestojson.resolvers.primitives.string.TextToEmptyStringResolver.EMPTY_TEXT_RESOLVER;
import static pl.jalokim.propertiestojson.resolvers.primitives.string.TextToJsonNullReferenceResolver.TEXT_TO_NULL_JSON_RESOLVER;
import static pl.jalokim.propertiestojson.resolvers.primitives.string.TextToStringResolver.TO_STRING_RESOLVER;
import static pl.jalokim.propertiestojson.util.PropertiesToJsonConverterBuilder.TO_JSON_TYPE_CONVERTERS;
import static pl.jalokim.propertiestojson.util.PropertiesToJsonConverterBuilder.TO_OBJECT_RESOLVERS;
import static pl.jalokim.propertiestojson.util.exception.ParsePropertiesException.PROPERTY_KEY_NEEDS_TO_BE_STRING_TYPE;
import static pl.jalokim.propertiestojson.util.exception.ParsePropertiesException.STRING_RESOLVER_AS_NOT_LAST;
import static pl.jalokim.propertiestojson.util.exception.ParsePropertiesException.STRING__TO_JSON_RESOLVER_AS_NOT_LAST;
public final class PropertiesToJsonConverter {
private final NullToJsonTypeConverter nullToJsonConverter;
private final TextToJsonNullReferenceResolver textToJsonNullResolver;
private final TextToEmptyStringResolver textToEmptyStringResolver;
private final Map algorithms = new HashMap<>();
private final PrimitiveJsonTypesResolver primitiveResolvers;
private PropertyKeysOrderResolver propertyKeysOrderResolver = new PropertyKeysOrderResolver();
/**
* Default implementation of json primitive type resolvers.
*/
public PropertiesToJsonConverter() {
this(TO_OBJECT_RESOLVERS, TO_JSON_TYPE_CONVERTERS);
}
/**
* This is deprecated constructor and will be remove in 6.0.0 version.
* Please migrate you resolvers for new interfaces and build PropertiesToJsonConverter instance through {@link PropertiesToJsonConverterBuilder}
*
* For add order or own implementation for for {@link TextToConcreteObjectResolver} please use one of below:
* - and use method {@link PropertiesToJsonConverterBuilder#onlyCustomTextToObjectResolvers}
* - and use method {@link PropertiesToJsonConverterBuilder#defaultAndCustomTextToObjectResolvers}
*
* For add order or own implementation for for {@link ObjectToJsonTypeConverter} please use one of below:
* - and use method {@link PropertiesToJsonConverterBuilder#onlyCustomObjectToJsonTypeConverters}
* - and use method {@link PropertiesToJsonConverterBuilder#defaultAndCustomObjectToJsonTypeConverters}
*
*
* @param customPrimitiveResolvers ordered list
*/
@Deprecated
public PropertiesToJsonConverter(PrimitiveJsonTypeResolver... customPrimitiveResolvers) {
this(convertToNewResolvers(customPrimitiveResolvers), convertToNewConverters(customPrimitiveResolvers));
}
private static List convertToNewConverters(PrimitiveJsonTypeResolver... customPrimitiveResolvers) {
validateTypeResolverOrder(customPrimitiveResolvers);
return Arrays.stream(customPrimitiveResolvers)
.map(PrimitiveJsonTypeResolverToNewApiAdapter::new)
.collect(Collectors.toList());
}
private static List convertToNewResolvers(PrimitiveJsonTypeResolver... customPrimitiveResolvers) {
return Arrays.stream(customPrimitiveResolvers)
.map(PrimitiveJsonTypeResolverToNewApiAdapter::new)
.collect(Collectors.toList());
}
public PropertiesToJsonConverter(List toObjectsResolvers,
List toJsonTypeResolvers) {
this(toObjectsResolvers, toJsonTypeResolvers, NULL_TO_JSON_RESOLVER, TEXT_TO_NULL_JSON_RESOLVER, EMPTY_TEXT_RESOLVER, false);
}
public PropertiesToJsonConverter(List toObjectsResolvers,
List toJsonTypeResolvers,
NullToJsonTypeConverter nullToJsonConverter,
TextToJsonNullReferenceResolver textToJsonNullResolver,
TextToEmptyStringResolver textToEmptyStringResolver,
Boolean skipNull) {
this.nullToJsonConverter = nullToJsonConverter;
this.textToJsonNullResolver = textToJsonNullResolver;
this.textToEmptyStringResolver = textToEmptyStringResolver;
validateTypeResolverOrder(toJsonTypeResolvers);
this.primitiveResolvers = new PrimitiveJsonTypesResolver(buildAllToObjectResolvers(toObjectsResolvers),
buildAllToJsonResolvers(toJsonTypeResolvers),
skipNull,
nullToJsonConverter);
algorithms.put(AlgorithmType.OBJECT, new ObjectJsonTypeResolver());
algorithms.put(AlgorithmType.PRIMITIVE, this.primitiveResolvers);
algorithms.put(AlgorithmType.ARRAY, new ArrayJsonTypeResolver());
}
/**
* Merged list of TextToConcreteObjectResolver instances for first conversion phase
*
* @param resolvers provided by user
* @return list
*/
private List buildAllToObjectResolvers(List resolvers) {
List allResolvers = new ArrayList<>();
allResolvers.add(textToJsonNullResolver);
allResolvers.add(textToEmptyStringResolver);
allResolvers.addAll(resolvers);
allResolvers.add(TO_STRING_RESOLVER);
return allResolvers;
}
/**
* Merged list of ObjectToJsonTypeConverter instances for second conversion phase
*
* @param toJsonTypeResolvers provided by user
* @return list
*/
private List buildAllToJsonResolvers(List toJsonTypeResolvers) {
List mergedToJsonTypeConverters = new ArrayList<>(toJsonTypeResolvers);
mergedToJsonTypeConverters.add(STRING_TO_JSON_RESOLVER);
mergedToJsonTypeConverters.add(nullToJsonConverter);
return mergedToJsonTypeConverters;
}
/**
* It generates Json from properties file stored in provided path as string.
* Every property value will tries resolve to concrete object by given resolvers...
* It will try convert to some object (number, boolean, list etc, depends on generic type of given {@link pl.jalokim.propertiestojson.resolvers.primitives.string.TextToConcreteObjectResolver}) from string value through method:
* {@link pl.jalokim.propertiestojson.resolvers.primitives.string.TextToConcreteObjectResolver#returnObjectWhenCanBeResolved(PrimitiveJsonTypesResolver primitiveJsonTypesResolver, String propertyValue, String propertyKey)}
* The order of resolvers is important because on that depends on which resolver as first will convert from string to some given object...
*
* Next will looks for sufficient converter, firstly will looks for exactly match class type,
* if not then will looks for closets parent class or parent interface.
* If will find resolver for parent class or parent interface at the same level, then will get parent super class as first.
* If will find only closets super interfaces (at the same level) then will throw exception...
* after successful found resolver it converts from given object to some instance which extends AbstractJsonType
* through method {@link pl.jalokim.propertiestojson.resolvers.primitives.object.ObjectToJsonTypeConverter#convertToJsonTypeOrEmpty(PrimitiveJsonTypesResolver primitiveJsonTypesResolver, Object propertyValue, String propertyKey)}
*
* @param pathToFile path to File
* @return simple String with json
* @throws ReadInputException when cannot find file
* @throws ParsePropertiesException when structure of properties is not compatible with json structure
*/
public String convertPropertiesFromFileToJson(String pathToFile) throws ReadInputException, ParsePropertiesException {
return convertPropertiesFromFileToJson(new File(pathToFile));
}
/**
* It generates Json from properties file stored in provided path as string and will converts only included keys or parts of property keys provided by second parameter.
* Every property value will tries resolve to concrete object by given resolvers...
* It will try convert to some object (number, boolean, list etc, depends on generic type of given {@link pl.jalokim.propertiestojson.resolvers.primitives.string.TextToConcreteObjectResolver}) from string value through method:
* {@link pl.jalokim.propertiestojson.resolvers.primitives.string.TextToConcreteObjectResolver#returnObjectWhenCanBeResolved(PrimitiveJsonTypesResolver primitiveJsonTypesResolver, String propertyValue, String propertyKey)}
* The order of resolvers is important because on that depends on which resolver as first will convert from string to some given object...
*
* Next will looks for sufficient converter, firstly will looks for exactly match class type,
* if not then will looks for closets parent class or parent interface.
* If will find resolver for parent class or parent interface at the same level, then will get parent super class as first.
* If will find only closets super interfaces (at the same level) then will throw exception...
* after successful found resolver it converts from given object to some instance which extends AbstractJsonType
* through method {@link pl.jalokim.propertiestojson.resolvers.primitives.object.ObjectToJsonTypeConverter#convertToJsonTypeOrEmpty(PrimitiveJsonTypesResolver primitiveJsonTypesResolver, Object propertyValue, String propertyKey)}
*
* @param pathToFile path to File
* @param includeDomainKeys domain head keys which should be parsed to json
* example properties:
* object1.field1=value1
* object1.field2=value2
* someObject2.field2=value3
* filter "object1"
* will parse only nested domain for "object1"
* @return simple String with json
* @throws ReadInputException when cannot find file
* @throws ParsePropertiesException when structure of properties is not compatible with json structure
*/
public String convertPropertiesFromFileToJson(String pathToFile, String... includeDomainKeys) throws ReadInputException, ParsePropertiesException {
return convertPropertiesFromFileToJson(new File(pathToFile), includeDomainKeys);
}
/**
* It generates Json from properties file stored in provided File.
* Every property value will tries resolve to concrete object by given resolvers...
* It will try convert to some object (number, boolean, list etc, depends on generic type of given {@link pl.jalokim.propertiestojson.resolvers.primitives.string.TextToConcreteObjectResolver}) from string value through method:
* {@link pl.jalokim.propertiestojson.resolvers.primitives.string.TextToConcreteObjectResolver#returnObjectWhenCanBeResolved(PrimitiveJsonTypesResolver primitiveJsonTypesResolver, String propertyValue, String propertyKey)}
* The order of resolvers is important because on that depends on which resolver as first will convert from string to some given object...
*
* Next will looks for sufficient converter, firstly will looks for exactly match class type,
* if not then will looks for closets parent class or parent interface.
* If will find resolver for parent class or parent interface at the same level, then will get parent super class as first.
* If will find only closets super interfaces (at the same level) then will throw exception...
* after successful found resolver it converts from given object to some instance which extends AbstractJsonType
* through method {@link pl.jalokim.propertiestojson.resolvers.primitives.object.ObjectToJsonTypeConverter#convertToJsonTypeOrEmpty(PrimitiveJsonTypesResolver primitiveJsonTypesResolver, Object propertyValue, String propertyKey)}
*
* @param file file with properties
* @return simple String with json
* @throws ReadInputException when cannot find file
* @throws ParsePropertiesException when structure of properties is not compatible with json structure
*/
public String convertPropertiesFromFileToJson(File file) throws ReadInputException, ParsePropertiesException {
try {
InputStream targetStream = new FileInputStream(file);
return convertToJson(targetStream);
} catch(FileNotFoundException e) {
throw new ReadInputException(e);
}
}
/**
* It generates Json from properties file stored in provided File and will converts only included keys or parts of property keys provided by second parameter.
* Every property value will tries resolve to concrete object by given resolvers...
* It will try convert to some object (number, boolean, list etc, depends on generic type of given {@link pl.jalokim.propertiestojson.resolvers.primitives.string.TextToConcreteObjectResolver}) from string value through method:
* {@link pl.jalokim.propertiestojson.resolvers.primitives.string.TextToConcreteObjectResolver#returnObjectWhenCanBeResolved(PrimitiveJsonTypesResolver primitiveJsonTypesResolver, String propertyValue, String propertyKey)}
* The order of resolvers is important because on that depends on which resolver as first will convert from string to some given object...
*
* Next will looks for sufficient converter, firstly will looks for exactly match class type,
* if not then will looks for closets parent class or parent interface.
* If will find resolver for parent class or parent interface at the same level, then will get parent super class as first.
* If will find only closets super interfaces (at the same level) then will throw exception...
* after successful found resolver it converts from given object to some instance which extends AbstractJsonType
* through method {@link pl.jalokim.propertiestojson.resolvers.primitives.object.ObjectToJsonTypeConverter#convertToJsonTypeOrEmpty(PrimitiveJsonTypesResolver primitiveJsonTypesResolver, Object propertyValue, String propertyKey)}
*
* @param file file with properties
* @param includeDomainKeys domain head keys which should be parsed to json
* example properties:
* object1.field1=value1
* object1.field2=value2
* someObject2.field2=value3
* filter "object1"
* will parse only nested domain for "object1"
* @return simple String with json
* @throws ReadInputException when cannot find file
* @throws ParsePropertiesException when structure of properties is not compatible with json structure
*/
public String convertPropertiesFromFileToJson(File file, String... includeDomainKeys) throws ReadInputException, ParsePropertiesException {
try {
InputStream targetStream = new FileInputStream(file);
return convertToJson(targetStream, includeDomainKeys);
} catch(FileNotFoundException e) {
throw new ReadInputException(e);
}
}
/**
* It generates Json from properties stored in provided InputStream.
* Every property value will tries resolve to concrete object by given resolvers...
* It will try convert to some object (number, boolean, list etc, depends on generic type of given {@link pl.jalokim.propertiestojson.resolvers.primitives.string.TextToConcreteObjectResolver}) from string value through method:
* {@link pl.jalokim.propertiestojson.resolvers.primitives.string.TextToConcreteObjectResolver#returnObjectWhenCanBeResolved(PrimitiveJsonTypesResolver primitiveJsonTypesResolver, String propertyValue, String propertyKey)}
* The order of resolvers is important because on that depends on which resolver as first will convert from string to some given object...
*
* Next will looks for sufficient converter, firstly will looks for exactly match class type,
* if not then will looks for closets parent class or parent interface.
* If will find resolver for parent class or parent interface at the same level, then will get parent super class as first.
* If will find only closets super interfaces (at the same level) then will throw exception...
* after successful found resolver it converts from given object to some instance which extends AbstractJsonType
* through method {@link pl.jalokim.propertiestojson.resolvers.primitives.object.ObjectToJsonTypeConverter#convertToJsonTypeOrEmpty(PrimitiveJsonTypesResolver primitiveJsonTypesResolver, Object propertyValue, String propertyKey)}
*
* @param inputStream InputStream with properties
* @return simple String with json
* @throws ReadInputException when cannot find file
* @throws ParsePropertiesException when structure of properties is not compatible with json structure
*/
public String convertToJson(InputStream inputStream) throws ReadInputException, ParsePropertiesException {
return convertToJson(inputStreamToProperties(inputStream));
}
/**
* It generates Json from properties stored in provided InputStream and will converts only included keys or parts of property keys provided by second parameter.
* Every property value will tries resolve to concrete object by given resolvers...
* It will try convert to some object (number, boolean, list etc, depends on generic type of given {@link pl.jalokim.propertiestojson.resolvers.primitives.string.TextToConcreteObjectResolver}) from string value through method:
* {@link pl.jalokim.propertiestojson.resolvers.primitives.string.TextToConcreteObjectResolver#returnObjectWhenCanBeResolved(PrimitiveJsonTypesResolver primitiveJsonTypesResolver, String propertyValue, String propertyKey)}
* The order of resolvers is important because on that depends on which resolver as first will convert from string to some given object...
*
* Next will looks for sufficient converter, firstly will looks for exactly match class type,
* if not then will looks for closets parent class or parent interface.
* If will find resolver for parent class or parent interface at the same level, then will get parent super class as first.
* If will find only closets super interfaces (at the same level) then will throw exception...
* after successful found resolver it converts from given object to some instance which extends AbstractJsonType
* through method {@link pl.jalokim.propertiestojson.resolvers.primitives.object.ObjectToJsonTypeConverter#convertToJsonTypeOrEmpty(PrimitiveJsonTypesResolver primitiveJsonTypesResolver, Object propertyValue, String propertyKey)}
*
* @param inputStream InputStream with properties
* @param includeDomainKeys domain head keys which should be parsed to json
* example properties:
* object1.field1=value1
* object1.field2=value2
* someObject2.field2=value3
* filter "object1"
* will parse only nested domain for "object1"
* @return simple String with json
* @throws ReadInputException when cannot find file
* @throws ParsePropertiesException when structure of properties is not compatible with json structure
*/
public String convertToJson(InputStream inputStream, String... includeDomainKeys) throws ReadInputException, ParsePropertiesException {
return convertToJson(inputStreamToProperties(inputStream), includeDomainKeys);
}
/**
* It generates Json from given Java Properties instance.
* If property value will be string then will not try convert it to another type.
*
* It will only looks for sufficient resolver, firstly will looks for exactly match class type,
* if not then will looks for closets parent class or parent interface.
* If will find resolver for parent class or parent interface at the same level, then will get parent super class as first.
* If will find only closets super interfaces (at the same level) then will throw exception...
* after successful found resolver it converts from given object to some instance which extends AbstractJsonType
* through method {@link pl.jalokim.propertiestojson.resolvers.primitives.object.ObjectToJsonTypeConverter#convertToJsonTypeOrEmpty(PrimitiveJsonTypesResolver primitiveJsonTypesResolver, Object propertyValue, String propertyKey)}
*
* @param properties Java Properties
* @return simple String with json
* @throws ParsePropertiesException when structure of properties is not compatible with json structure
*/
public String convertToJson(Properties properties) throws ParsePropertiesException {
for(Map.Entry