mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
add toYamlCustomized / toJsonCustomized methods, that expose jackson's now-preferred Builder objects to be configured on instead of doing config directly on mapper objects.
This commit is contained in:
@ -37,6 +37,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
import com.fasterxml.jackson.databind.ObjectWriter;
|
import com.fasterxml.jackson.databind.ObjectWriter;
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||||
|
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
@ -84,6 +85,7 @@ public class JsonUtils
|
|||||||
** Internally using jackson - so jackson annotations apply!
|
** Internally using jackson - so jackson annotations apply!
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@Deprecated(since = "since toJsonCustomized was added, which uses jackson's newer builder object for customization")
|
||||||
public static String toJson(Object object, Consumer<ObjectMapper> objectMapperCustomizer)
|
public static String toJson(Object object, Consumer<ObjectMapper> objectMapperCustomizer)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -105,6 +107,34 @@ public class JsonUtils
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Serialize any object into a JSON String - with customizations on the Jackson
|
||||||
|
** ObjectMapper.
|
||||||
|
**
|
||||||
|
** Internally using jackson - so jackson annotations apply!
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static String toJsonCustomized(Object object, Consumer<JsonMapper.Builder> jsonMapperCustomizer)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JsonMapper.Builder jsonMapperBuilder = newJsonMapperBuilder();
|
||||||
|
if(jsonMapperCustomizer != null)
|
||||||
|
{
|
||||||
|
jsonMapperCustomizer.accept(jsonMapperBuilder);
|
||||||
|
}
|
||||||
|
String jsonResult = jsonMapperBuilder.build().writeValueAsString(object);
|
||||||
|
return (jsonResult);
|
||||||
|
}
|
||||||
|
catch(JsonProcessingException e)
|
||||||
|
{
|
||||||
|
LOG.error("Error serializing object of type [" + object.getClass().getSimpleName() + "] to json", e);
|
||||||
|
throw new IllegalArgumentException("Error in JSON Serialization", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Serialize any object into a "pretty" / formatted JSON String.
|
** Serialize any object into a "pretty" / formatted JSON String.
|
||||||
**
|
**
|
||||||
@ -168,7 +198,6 @@ public class JsonUtils
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** De-serialize a json string into an object of the specified class - with
|
** De-serialize a json string into an object of the specified class - with
|
||||||
** customizations on the Jackson ObjectMapper.
|
** customizations on the Jackson ObjectMapper.
|
||||||
**.
|
|
||||||
**
|
**
|
||||||
** Internally using jackson - so jackson annotations apply!
|
** Internally using jackson - so jackson annotations apply!
|
||||||
**
|
**
|
||||||
@ -242,6 +271,23 @@ public class JsonUtils
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Standard private method to build jackson JsonMapperBuilder with standard features.
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static JsonMapper.Builder newJsonMapperBuilder()
|
||||||
|
{
|
||||||
|
JsonMapper.Builder jsonMapperBuilder = JsonMapper.builder();
|
||||||
|
jsonMapperBuilder.addModule(new JavaTimeModule());
|
||||||
|
jsonMapperBuilder.serializationInclusion(JsonInclude.Include.NON_NULL);
|
||||||
|
jsonMapperBuilder.serializationInclusion(JsonInclude.Include.NON_EMPTY);
|
||||||
|
jsonMapperBuilder.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
|
||||||
|
jsonMapperBuilder.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
|
||||||
|
|
||||||
|
return (jsonMapperBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Standard private method to build jackson ObjectMapper with standard features.
|
** Standard private method to build jackson ObjectMapper with standard features.
|
||||||
**
|
**
|
||||||
|
@ -26,9 +26,11 @@ import java.util.Map;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.MapperFeature;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||||
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
|
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
|
||||||
|
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
|
|
||||||
|
|
||||||
@ -62,7 +64,7 @@ public class YamlUtils
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static String toYaml(Object object)
|
public static String toYaml(Object object)
|
||||||
{
|
{
|
||||||
return toYaml(object, null);
|
return toYamlCustomized(object, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -70,6 +72,7 @@ public class YamlUtils
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@Deprecated(since = "since toYamlCustomized was added, which uses jackson's newer builder object for customization")
|
||||||
public static String toYaml(Object object, Consumer<ObjectMapper> objectMapperCustomizer)
|
public static String toYaml(Object object, Consumer<ObjectMapper> objectMapperCustomizer)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -96,4 +99,36 @@ public class YamlUtils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static String toYamlCustomized(Object object, Consumer<YAMLMapper.Builder> yamlMapperCustomizer)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
YAMLFactory yamlFactory = new YAMLFactory()
|
||||||
|
.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);
|
||||||
|
|
||||||
|
YAMLMapper.Builder yamlMapperBuilder = YAMLMapper.builder(yamlFactory);
|
||||||
|
yamlMapperBuilder.serializationInclusion(JsonInclude.Include.NON_NULL);
|
||||||
|
yamlMapperBuilder.serializationInclusion(JsonInclude.Include.NON_EMPTY);
|
||||||
|
|
||||||
|
if(yamlMapperCustomizer != null)
|
||||||
|
{
|
||||||
|
yamlMapperCustomizer.accept(yamlMapperBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
YAMLMapper yamlMapper = yamlMapperBuilder.build();
|
||||||
|
yamlMapper.findAndRegisterModules();
|
||||||
|
return (yamlMapper.writeValueAsString(object));
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
LOG.error("Error serializing object of type [" + object.getClass().getSimpleName() + "] to yaml", e);
|
||||||
|
throw new IllegalArgumentException("Error in YAML Serialization", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,9 +24,11 @@ package com.kingsrook.qqq.backend.core.utils;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.MapperFeature;
|
||||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -60,6 +62,47 @@ class YamlUtilsTest extends BaseTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* simple bean to use in customObjectMapper test
|
||||||
|
* we'd use a map, but SORT_PROPERTIES_ALPHABETICALLY doesn't apply to maps...
|
||||||
|
***************************************************************************/
|
||||||
|
private record SomeBean(String foo, Integer bar) {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testCustomObjectMapper() throws JsonProcessingException
|
||||||
|
{
|
||||||
|
SomeBean someBean = new SomeBean("Hi", 47);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////
|
||||||
|
// by default, the fields come out in the order they're declared //
|
||||||
|
///////////////////////////////////////////////////////////////////
|
||||||
|
assertEquals("""
|
||||||
|
foo: "Hi"
|
||||||
|
bar: 47
|
||||||
|
""", YamlUtils.toYaml(someBean));
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
// customize the builder to sort properties alphabetically //
|
||||||
|
// (to assert that doing a customization works) //
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
String outputYaml = YamlUtils.toYamlCustomized(someBean, yamlMapperBuilder ->
|
||||||
|
{
|
||||||
|
yamlMapperBuilder.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
assertEquals("""
|
||||||
|
bar: 47
|
||||||
|
foo: "Hi"
|
||||||
|
""", outputYaml);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -104,7 +104,7 @@ public class PublishAPI implements Callable<Integer>
|
|||||||
// subsets of it (e.g., grouped by table mostly) - then we'll write out each such file //
|
// subsets of it (e.g., grouped by table mostly) - then we'll write out each such file //
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
OpenAPI openAPI = middlewareVersion.generateOpenAPIModel("qqq");
|
OpenAPI openAPI = middlewareVersion.generateOpenAPIModel("qqq");
|
||||||
String yaml = YamlUtils.toYaml(openAPI, mapper ->
|
String yaml = YamlUtils.toYamlCustomized(openAPI, mapperBuilder ->
|
||||||
{
|
{
|
||||||
if(sortFileContentsForHuman)
|
if(sortFileContentsForHuman)
|
||||||
{
|
{
|
||||||
@ -114,7 +114,7 @@ public class PublishAPI implements Callable<Integer>
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
|
mapperBuilder.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -112,9 +112,9 @@ public class ValidateAPIVersions implements Callable<Integer>
|
|||||||
// generate a new spec based on current code in codebase //
|
// generate a new spec based on current code in codebase //
|
||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
OpenAPI openAPI = middlewareVersion.generateOpenAPIModel("qqq");
|
OpenAPI openAPI = middlewareVersion.generateOpenAPIModel("qqq");
|
||||||
String yaml = YamlUtils.toYaml(openAPI, mapper ->
|
String yaml = YamlUtils.toYamlCustomized(openAPI, mapperBuilder ->
|
||||||
{
|
{
|
||||||
mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
|
mapperBuilder.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
Reference in New Issue
Block a user