How to dynamically convert JSON to XML with custom namespace? - java

I try to convert a JSON into an XML with the following code
final ObjectMapper objectMapper = new ObjectMapper();
final XmlMapper xmlMapper = new XmlMapper();
JsonNode jsonNode = objectMapper.readTree(jsonString);
String xmlString = xmlMapper
.writerWithDefaultPrettyPrinter()
.withRootName("rootname")
.writeValueAsString(jsonNode);
Basically it works. Does anyone know, how I can add a namespace to the serialized XML-attributes. I've no POJOs for the objects. The convert should generate from this
{
"Status" : "OK"
}
something like this:
<ns2:rootname xmlns:ns2="http://whatever-it-is.de/">
<ns2:state>OK</ns2:state>
</ns2:rootname>

just create a pojo and add jackson annotations, e.g.
#JacksonXmlProperty(localName="ns2:http://whatever-it-is.de/")
public class Status {
// ...
}
Or if you want to go without a pojo try a custom serializer which adds namespaces
https://www.baeldung.com/jackson-custom-serialization

You need to provide custom Json Node serialiser and use ToXmlGenerator. See below example:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import javax.xml.namespace.QName;
import java.io.IOException;
public class XmlMapperApp {
public static void main(String... args) throws Exception {
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.enable(SerializationFeature.INDENT_OUTPUT);
ObjectNode node = xmlMapper.createObjectNode()
.put("Status", "OK")
.set("node", xmlMapper.createObjectNode()
.put("int", 1)
.put("str", "str"));
SimpleModule module = new SimpleModule();
module.setSerializerModifier(new BeanSerializerModifier() {
#Override
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
if (beanDesc.getType().getRawClass().equals(ObjectNode.class)) {
return new ObjectNodeJsonSerializer(serializer);
}
return super.modifySerializer(config, beanDesc, serializer);
}
});
xmlMapper.registerModule(module);
System.out.println(xmlMapper.writeValueAsString(node));
}
}
class ObjectNodeJsonSerializer extends JsonSerializer<JsonNode> {
private final JsonSerializer baseSerializer;
ObjectNodeJsonSerializer(JsonSerializer baseSerializer) {
this.baseSerializer = baseSerializer;
}
#Override
public void serialize(JsonNode value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
ToXmlGenerator xmlGenerator = (ToXmlGenerator) gen;
xmlGenerator.setNextName(new QName("http://whatever-it-is.de/", "rootname", "anything"));
baseSerializer.serialize(value, gen, serializers);
}
}
Above example prints:
<wstxns1:rootname xmlns:wstxns1="http://whatever-it-is.de/">
<wstxns1:Status>OK</wstxns1:Status>
<wstxns1:node>
<wstxns1:int>1</wstxns1:int>
<wstxns1:str>str</wstxns1:str>
</wstxns1:node>
</wstxns1:rootname>

Underscore-java library can convert JSON to XML with namespace.
{
"ns2:rootname": {
"-xmlns:ns2": "http://whatever-it-is.de/",
"ns2:state": "OK"
},
"#omit-xml-declaration": "yes"
}
<ns2:rootname xmlns:ns2="http://whatever-it-is.de/">
<ns2:state>OK</ns2:state>
</ns2:rootname>

Related

Is there any way I can resolve java heap memory error when converting a large csv file to JSON?

I'm writing the below to convert CSV file to JSON but getting java heap memory error.
Can anyone help me to write using the Jackson stream API?
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.databind.MappingIterator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
public class JacksonProgram {
public static void main(String[] args) throws Exception {
File input = new File("C:\\Users\\shivamurthym\\Downloads\\FL_insurance_sample\\FL_insurance_sample.csv");
File output = new File("C:\\Users\\shivamurthym\\data.json");
List<Map<?, ?>> data = readObjectsFromCsv(input);
writeAsJson(data, output);
}
public static List<Map<?, ?>> readObjectsFromCsv(File file) throws IOException {
CsvSchema bootstrap = CsvSchema.emptySchema().withHeader();
CsvMapper csvMapper = new CsvMapper();
MappingIterator<Map<?, ?>> mappingIterator = csvMapper.reader(Map.class).with(bootstrap).readValues(file);
return mappingIterator.readAll();
}
public static void writeAsJson(List<Map<?, ?>> data, File file) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(file, data);
}
}

How to Deserialize Array of JSON Objects in Java

So I'm used to getting JSON objects from a given API/endpoint, e.g.:
{
"count": 5,
"results": [
{
"example": "test",
"is_valid": true
},
{
"example": "test2",
"is_valid": true
}
]
}
And in a custom deserializer that extends com.fasterxml.jackson.databind.deser.std.StdDeserializer, I know I can use the JsonParser object like so to get the base node to work off of, i.e.:
#Override
public ResultExample deserialize(JsonParser jp, DeserializationContext ctxt) {
JsonNode node = jp.getCodec().readTree(jp);
JsonNode count = node.get("count");
// code to put parsed objects into a ResultExample object...
}
However, I just encountered an API that simply returns an array of JSON objects, e.g.:
[
{
"example": "test",
"is_valid": true
},
{
"example": "test2",
"is_valid": true
},
]
So, I don't believe I can just parse this the same way as before. What would be the correct way to parse this using Jackson?
This may help you:
import java.io.IOException;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Test {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
String json = "[\r\n" + " {\r\n" + " \"example\": \"test\",\r\n" + " \"is_valid\": true\r\n"
+ " },\r\n" + " {\r\n" + " \"example\": \"test2\",\r\n" + " \"is_valid\": true\r\n"
+ " }\r\n" + "]";
Example[] ex = mapper.readValue(json, Example[].class);
}
}
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({ "example", "is_valid" })
class Example {
#JsonProperty("example")
private String example;
#JsonProperty("is_valid")
private Boolean isValid;
public String getExample() {
return example;
}
#JsonProperty("example")
public void setExample(String example) {
this.example = example;
}
#JsonProperty("is_valid")
public Boolean getIsValid() {
return isValid;
}
#JsonProperty("is_valid")
public void setIsValid(Boolean isValid) {
this.isValid = isValid;
}
}
When response is a JSON Object you can use default bean deserialiser. In case it is a JSON Array you can read it as array and create response object manually. Below you can find example custom deserialiser and BeanDeserializerModifier which is used to register custom deserialiser. BeanDeserializerModifier allows to use default deserialiser when JSON payload fits POJO model:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.BeanDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializerBase;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.CollectionType;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class JsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
SimpleModule module = new SimpleModule();
module.setDeserializerModifier(new BeanDeserializerModifier() {
#Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
if (beanDesc.getBeanClass() == Response.class) {
return new ResponseJsonDeserializer((BeanDeserializerBase) deserializer);
}
return super.modifyDeserializer(config, beanDesc, deserializer);
}
});
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
System.out.println(mapper.readValue(jsonFile, Response.class));
}
}
class ResponseJsonDeserializer extends BeanDeserializer {
private final BeanDeserializerBase baseDeserializer;
protected ResponseJsonDeserializer(BeanDeserializerBase src) {
super(src);
this.baseDeserializer = src;
}
#Override
public Response deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
if (p.currentToken() == JsonToken.START_OBJECT) {
return (Response) baseDeserializer.deserialize(p, ctxt);
}
if (p.currentToken() == JsonToken.START_ARRAY) {
CollectionType collectionType = ctxt.getTypeFactory().constructCollectionType(List.class, Item.class);
JsonDeserializer<Object> deserializer = ctxt.findRootValueDeserializer(collectionType);
List<Item> results = (List<Item>) deserializer.deserialize(p, ctxt);
Response response = new Response();
response.setCount(results.size());
response.setResults(results);
return response;
}
throw MismatchedInputException.from(p, Response.class, "Only object or array!");
}
}
class Response {
private int count;
private List<Item> results;
// getters, setters, toString
}
class Item {
private String example;
#JsonProperty("is_valid")
private boolean isValid;
// getters, setters, toString
}
Above code for JSON Object payload prints:
Response{count=5, results=[Item{example='test', isValid=true}, Item{example='test2', isValid=true}]}
And for JSON Array payload prints:
Response{count=2, results=[Item{example='test', isValid=true}, Item{example='test2', isValid=true}]}
Guess I should have just written the unit test before asking the question, but apparently you can just do it the same way. Only difference is the base node is a JsonArray which you have to iterate over. Thanks everyone who looked into this.

Transforming JSON to XML without converting decimal to scientific notation

I am trying to convert JSON to XML in middle ware tool. I am using Jackson libraries to do this transformation. The problem is that for decimal fields (length more than 8) in JSON, the corresponding XML value is converted to scientific notation. For example 8765431002.13 is converted to 8.76543100213E8.
I can convert the scientific notation to normal decimal format if know the name of the field. But in my case, the middleware application will not be aware of field that is coming as decimal.
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
public class JSONDataformat {
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
//String jsonString = "{\"Field1\":18629920.68,\"Field3\":\"test\", \"Field2\":\"null\"}";
ObjectMapper objectMapper = new ObjectMapper();
ObjectMapper xmlMapper = new XmlMapper();
JsonNode tree = objectMapper.readTree(jsonString);
String jsonAsXml = xmlMapper.writer().writeValueAsString(tree);
System.out.println(jsonAsXml);
}
catch(Exception e) {e.printStackTrace(); }
}
}
Output
<ObjectNode xmlns=""><Field1>1.862992068E7</Field1><Field3>test</Field3><Field2/></ObjectNode>
I expected to get <Field1> value as 18629920.68 in above code.
You need to enable USE_BIG_DECIMAL_FOR_FLOATS feature:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
EDIT
import com.fasterxml.jackson.core.JsonGenerator.Feature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
String jsonString = "{\"Field1\": 20121220.00,\"Field3\":\"test\", \"Field2\":\"null\"}";
ObjectMapper jsonMapper = new ObjectMapper();
jsonMapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
XmlMapper xmlMapper = new XmlMapper();
JsonNode tree = jsonMapper.readTree(jsonString);
String jsonAsXml = xmlMapper.writer().with(Feature.WRITE_BIGDECIMAL_AS_PLAIN).writeValueAsString(tree);
System.out.println(jsonAsXml);
}
}
Above code prints:
<ObjectNode><Field1>20121220</Field1><Field3>test</Field3><Field2>null</Field2></ObjectNode>

Dynamic Jackson Custom Deserializer

My JSON looks like this:
{"typeName":"test","field":{"name":"42"}}
I have two deserializers. The first one (JsonDeserializer<EntityImpl>) will examine the JSON and extract a type information which is provided by the typeName property.
The second deserializer (JsonDeserializer<TestField>) is used to deserialize the field property. This deserializer needs to know the previously extracted typeName value in order to work correctly.
How can i pass-along the type information from one deserializer to other deserializers? I tried to use DeserializationContext but i don't know how to pass along the Context from deserializer A to B.
My current code looks like this:
EntityImpl.java:
package de.jotschi.test;
public class EntityImpl implements Entity {
private String typeName;
private TestField field;
public String getTypeName() {
return typeName;
}
public void setTypeName(String typeName) {
this.typeName = typeName;
}
public TestField getField() {
return field;
}
public void setField(TestField field) {
this.field = field;
}
}
TestField.java:
package de.jotschi.test;
public class TestField {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Test:
package de.jotschi.test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import de.jotschi.test.EntityImpl;
import de.jotschi.test.TestField;
public class TestMapper2 {
private InjectableValues getInjectableValue() {
InjectableValues values = new InjectableValues() {
#Override
public Object findInjectableValue(Object valueId, DeserializationContext ctxt, BeanProperty forProperty, Object beanInstance) {
if ("data".equals(valueId.toString())) {
return new HashMap<String, String>();
}
return null;
}
};
return values;
}
#Test
public void testMapper() throws IOException {
ObjectMapper mapper = new ObjectMapper();
SimpleModule idAsRefModule = new SimpleModule("ID-to-ref", new Version(1, 0, 0, null));
idAsRefModule.addDeserializer(EntityImpl.class, new JsonDeserializer<EntityImpl>() {
#Override
public EntityImpl deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
Map<String, String> dataMap = (Map) ctxt.findInjectableValue("data", null, null);
System.out.println("Value: " + dataMap.get("test"));
ObjectCodec codec = jp.getCodec();
JsonNode node = codec.readTree(jp);
String type = node.get("typeName").asText();
dataMap.put("typeName", type);
// How to pass on type information to TestField deserializer? The context is not reused for the next deserializer.
// I assume that readValueAs fails since the codec.readTree method has already been invoked.
//return jp.readValueAs(EntityImpl.class);
// Alternatively the treeToValue method can be invoked in combination with the node. Unfortunately all information about the DeserializationContext is lost. I assume new context will be created.
// How to reuse the old context?
return codec.treeToValue(node, EntityImpl.class);
}
});
idAsRefModule.addDeserializer(TestField.class, new JsonDeserializer<TestField>() {
#Override
public TestField deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
// Access type from context
Map<String, String> dataMap = (Map) ctxt.findInjectableValue("data", null, null);
System.out.println(dataMap.get("typeName"));
ObjectCodec codec = p.getCodec();
JsonNode node = codec.readTree(p);
return codec.treeToValue(node, TestField.class);
}
});
mapper.registerModule(idAsRefModule);
mapper.setSerializationInclusion(Include.NON_NULL);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// Setup the pojo
EntityImpl impl = new EntityImpl();
impl.setTypeName("test");
TestField testField = new TestField();
testField.setName("42");
impl.setField(testField);
// POJO -> JSON
String json = mapper.writeValueAsString(impl);
System.out.println(json);
// JSON -> POJO
Entity obj = mapper.reader(getInjectableValue()).forType(EntityImpl.class).readValue(json);
System.out.println(obj.getClass().getName());
}
}
My current solution is call the following mapper this way:
return mapper.setInjectableValues(getInjectableValue(dataMap)).treeToValue(obj, EntityImpl.class);
This way the previously loaded context data map is put into a new context that is used for the following parsing process.
Full example:
package de.jotschi.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class TestMapper2 {
private InjectableValues getInjectableValue(final Map<String, String> dataMap) {
InjectableValues values = new InjectableValues() {
#Override
public Object findInjectableValue(Object valueId, DeserializationContext ctxt, BeanProperty forProperty, Object beanInstance) {
if ("data".equals(valueId.toString())) {
return dataMap;
}
return null;
}
};
return values;
}
#Test
public void testMapper() throws IOException {
ObjectMapper mapper = new ObjectMapper();
SimpleModule idAsRefModule = new SimpleModule("ID-to-ref", new Version(1, 0, 0, null));
idAsRefModule.addDeserializer(Entity.class, new JsonDeserializer<Entity>() {
#Override
public Entity deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
Map<String, String> dataMap = (Map) ctxt.findInjectableValue("data", null, null);
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
ObjectNode obj = (ObjectNode) mapper.readTree(jp);
String type = obj.get("typeName").asText();
dataMap.put("typeName", type);
return mapper.setInjectableValues(getInjectableValue(dataMap)).treeToValue(obj, EntityImpl.class);
}
});
idAsRefModule.addDeserializer(TestField.class, new JsonDeserializer<TestField>() {
#Override
public TestField deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
// Access type from context
Map<String, String> dataMap = (Map) ctxt.findInjectableValue("data", null, null);
System.out.println("Type name: " + dataMap.get("typeName"));
ObjectMapper mapper = (ObjectMapper) p.getCodec();
ObjectNode obj = (ObjectNode) mapper.readTree(p);
// Custom deserialisation
TestField field = new TestField();
field.setName(obj.get("name").asText());
// Delegate further deserialisation to other mapper
field.setSubField(mapper.setInjectableValues(getInjectableValue(dataMap)).treeToValue(obj.get("subField"), SubField.class));
return field;
}
});
mapper.registerModule(idAsRefModule);
mapper.setSerializationInclusion(Include.NON_NULL);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// Setup the pojo
EntityImpl impl = new EntityImpl();
impl.setTypeName("test");
TestField testField = new TestField();
testField.setName("42");
SubField subField = new SubField();
subField.setName("sub");
testField.setSubField(subField);
impl.setField(testField);
// POJO -> JSON
String json = mapper.writeValueAsString(impl);
System.out.println(json);
// JSON -> POJO
Entity obj = mapper.reader(getInjectableValue(new HashMap<String, String>())).forType(Entity.class).readValue(json);
assertNotNull("The enity must not be null", obj);
assertNotNull(((EntityImpl) obj).getField());
assertEquals("42", ((EntityImpl) obj).getField().getName());
assertNotNull(((EntityImpl) obj).getField().getSubField());
assertEquals("sub", ((EntityImpl) obj).getField().getSubField().getName());
System.out.println(obj.getClass().getName());
}
}

How to unit test a custom Jackson JsonSerializer?

I wrote the following JsonSerializer to let Jackson serialize an array of integers into JSON:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class TalkIdsSerializer extends JsonSerializer<TalkIds> {
/**
* Serializes a TalkIds object into the following JSON string:
* Example: { "talk_ids" : [ 5931, 5930 ] }
*/
#Override
public void serialize(TalkIds talkIds, JsonGenerator jsonGenerator,
SerializerProvider provider)
throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeArrayFieldStart(TalkIds.API_DICTIONARY_KEY);
for (Integer talkId : talkIds.getTalkIds()) {
jsonGenerator.writeNumber(talkId);
}
jsonGenerator.writeEndArray();
jsonGenerator.writeEndObject();
}
}
The class is used here:
#JsonSerialize(using = TalkIdsSerializer.class)
public class TalkIds { /* ... */ }
I want test the behavior of the serializer and came up with the following:
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class TalkIdsSerializerTest {
protected final ArrayList<Integer> TALK_IDS =
new ArrayList<>(Arrays.asList(5931, 5930));
protected TalkIdsSerializer talkIdsSerializer;
#Before
public void setup() throws IOException {
talkIdsSerializer = new TalkIdsSerializer();
}
#Test
public void testSerialize() throws IOException {
StringWriter stringWriter = new StringWriter();
JsonGenerator jsonGenerator =
new JsonFactory().createGenerator(stringWriter);
TalkIds talkIds = new TalkIds();
talkIds.add(TALK_IDS);
talkIdsSerializer.serialize(talkIds, jsonGenerator, null);
String string = stringWriter.toString(); // string is ""
assertNotNull(string);
assertTrue(string.length() > 0);
stringWriter.close();
}
}
However, nothing is written to the StringWriter. What am I doing wrong?
You need to flush() the generator
Method called to flush any buffered content to the underlying target (output stream, writer), and to flush the target itself as well.
http://fasterxml.github.io/jackson-core/javadoc/2.1.0/com/fasterxml/jackson/core/JsonGenerator.html#flush()
I had a similar requirement, to test a custom serializer. I used objectMapper to get the string directly(since you have already annotated TalkIds with JsonSerialize). You can get the json string from the object as follows
String json = new ObjectMapper().writeValueAsString(talkIds)
For me flush() changed nothing, so I changed the way to test it, in accordance with http://www.baeldung.com/jackson-custom-serialization.
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.io.StringWriter;
//...
#Test
public void serialize_custom() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(MyCustomSerializer.class, myCustomSerializer);
objectMapper.registerModule(module);
StringWriter stringWriter = new StringWriter();
TalkIds talkIds = new TalkIds();
talkIds.add(TALK_IDS);
objectMapper.writeValue(stringWriter,wi);
assertTrue(stringWriter.toString().length() > 3);
}

Categories