How to deserialize multiple objects sequentially using jackson-databind - java

I am using msgpack to serialize data. I have some code works fine with serializing data.
public void testJackson() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
String data1 = "test data";
int data2 = 10;
List<String> data3 = new ArrayList<String>();
data3.add("list data1");
data3.add("list data1");
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(out, data1);
mapper.writeValue(out, data2);
mapper.writeValue(out, data3);
// TODO: How to deserialize?
}
But now I don't know how to deserialize data.
I am not finding any solution anywhere. It will be good if anyone can help how to proceed.

The problem
I have tried many of the readValue methods, but I only can get the first String, about the second and third value I have no idea
The thing is, Jackson always reads the first data, since the data is neither deleted from the nor did you explicitly tell Jackson that the next data is from position A to position B
Solutions
this example works and is similar to your code, but is not very elegant. Here I explicitly tell Jackson where my data is, but I have to know how it got written, which is a way too specific solution
File dataFile = new File("jackson.txt");
if(!dataFile.exists())
dataFile.createNewFile();
FileOutputStream fileOut = new FileOutputStream(dataFile);
ByteArrayOutputStream out = new ByteArrayOutputStream();
FileInputStream fileIn = new FileInputStream(dataFile);
String writeData1 = "test data";
int writeData2 = 10;
List<String> writeData3 = new ArrayList<String>();
writeData3.add("list data1");
writeData3.add("list data1");
ObjectMapper mapper = new ObjectMapper();
byte[] writeData1Bytes = mapper.writeValueAsBytes(writeData1);
out.write(writeData1Bytes);
byte[] writeData2Bytes = mapper.writeValueAsBytes(writeData2);
out.write(writeData2Bytes);
byte[] writeData3Bytes = mapper.writeValueAsBytes(writeData3);
out.write(writeData3Bytes);
out.writeTo(fileOut);
// TODO: How to deserialize?
int pos = 0;
byte[] readData = new byte[1000];
fileIn.read(readData);
String readData1 = mapper.readValue(readData, pos, writeData1Bytes.length, String.class);
pos += writeData1Bytes.length;
Integer readData2 = mapper.readValue(readData, pos, writeData2Bytes.length, Integer.class);
pos += writeData2Bytes.length;
ArrayList readData3 = mapper.readValue(readData, pos, writeData3Bytes.length, ArrayList.class);
pos += writeData3Bytes.length;
System.out.printf("readData1 = %s%n", readData1);
System.out.printf("readData2 = %s%n", readData2);
System.out.printf("readData3 = %s%n", readData3);
the file looks then like this
"test data"10["list data1","list data1"]
How to do it correctly
a way more elegant way is to encapsulate your data in an object which can be turned into a valid JSON string and from that Jackson won't need any more information
public class JacksonTest {
public static class DataNode {
#JsonProperty("data1")
private String data1;
#JsonProperty("data2")
private int data2;
#JsonProperty("data3")
private List<String> data3;
//needed for Jackson
public DataNode() {
}
public DataNode(String data1, int data2, List<String> data3) {
this.data1 = data1;
this.data2 = data2;
this.data3 = data3;
}
}
public static void main(String[] args) throws Exception {
File dataFile = new File("jackson.txt");
if(!dataFile.exists())
dataFile.createNewFile();
FileOutputStream fileOut = new FileOutputStream(dataFile);
ByteArrayOutputStream out = new ByteArrayOutputStream();
FileInputStream fileIn = new FileInputStream(dataFile);
String writeData1 = "test data";
int writeData2 = 10;
List<String> writeData3 = new ArrayList<String>();
writeData3.add("list data1");
writeData3.add("list data1");
DataNode writeData = new DataNode(writeData1, writeData2, writeData3);
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(out, writeData);
out.writeTo(fileOut);
// TODO: How to deserialize?
DataNode readData = mapper.readValue(fileIn, DataNode.class);
System.out.printf("readData1 = %s%n", readData.data1);
System.out.printf("readData2 = %s%n", readData.data2);
System.out.printf("readData3 = %s%n", readData.data3);
}
}
the content of the file looks like this
{"data1":"test data","data2":10,"data3":["list data1","list data1"]}

You'll want to use one of the readValue methods from ObjectMapper - probably one that has a Reader or InputStream as the first parameter.

#Japu_D_Cret Thank you for such a detailed answer!
Actually I want to use msgpack to transfer data, and I made it work by using msgpack, here is my code
ByteArrayOutputStream out = new ByteArrayOutputStream();
String data1 = "test data";
int data2 = 10;
List<String> data3 = new ArrayList<String>();
data3.add("list data1");
data3.add("list data1");
MessagePack packer = new MessagePack();
packer.write(out, data1);
packer.write(out, data2);
packer.write(out, data3);
// TODO: How to deserialize?
BufferUnpacker unpacker = packer.createBufferUnpacker(out.toByteArray());
System.out.println(unpacker.readString());
System.out.println(unpacker.readInt());
System.out.println(unpacker.read(Templates.tList(Templates.TString)));
Then I found jackson-databind on msgpack website and it supports msgpack format also.
I do some tests on these two and found that jackson's serialize performance is better than msgpack, so I want to use jackson instead of msgpack.

Related

A special character added during ByteArrayOutputStream conversion

I have a XmlObject which has the correct value what i needed.
Ex : 1½-2Y
But when i tried to convert it into byte of stream, the result i am seeing as 1½-2Y.
Sample code :
import org.apache.xmlbeans.XmlObject;
Class MyClass implements XmlBuilder<T> {
protected final String serializeToXml(XmlObject xmlObject) {
ByteArrayOutputStream os = null;
try {
os = new ByteArrayOutputStream();
xmlObject.save(os,createXmlOptions()); /Its adding a special char here
return os.toString();
}
}
protected final XmlOptions createXmlOptions() {
final XmlOptions xmlOptions = new XmlOptions();
xmlOptions.setValidateOnSet();
xmlOptions.setCharacterEncoding(UTF_8_ENCODING);
return xmlOptions;
}
}
os.toString() will internally call new String(buffer) and thus will use the system encoding which I assume is not UTF-8.
In general you should explicitly provide the encoding, e.g. new String( os.toByteArray(), "UTF-8").

How to properly parse streaming JSON with Jackson?

I'm trying to figure out a clean way to parse streaming JSON with Jackson. "Streaming" as in TCP, off-the-wire, in a piecemeal fashion without any guarantee of receiving complete JSON data in a single read (no message framing either). Also, the goal is to do this asynchronously, which rules out relying on Jackson's handling of java.io.InputStreams. I came up with a functioning solution (see demonstration below), but I'm not particularly happy with it. Imperative style aside, I don't like the ungraceful handling of incomplete JSON by JsonParser#readValueAsTree. When processing a stream of bytes, incomplete data is absolutely normal and is not an exceptional scenario, so it's strange (and unacceptable) to see java.io.IOExceptions in Jackson's APIs. I also looked into using Jackson's TokenBuffer, but ran into similar issues. Is Jackson not really meant for processing true streaming JSON?
package com.example.jackson;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.emptyList;
public class AsyncJsonParsing {
public static void main(String[] args) {
final AsyncJsonParsing parsing = new AsyncJsonParsing();
parsing.runFirstScenario();
parsing.runSecondScenario();
parsing.runThirdScenario();
parsing.runFourthScenario();
}
static final class ParsingOutcome {
final List<JsonNode> roots;//list of parsed JSON objects and JSON arrays
final byte[] remainder;
ParsingOutcome(final List<JsonNode> roots, final byte[] remainder) {
this.roots = roots;
this.remainder = remainder;
}
}
final byte[] firstMessage = "{\"message\":\"first\"}".getBytes(UTF_8);
final byte[] secondMessage = "{\"message\":\"second\"}".getBytes(UTF_8);
final byte[] leadingHalfOfFirstMessage = Arrays.copyOfRange(firstMessage, 0, firstMessage.length / 2);
final byte[] trailingHalfOfFirstMessage = Arrays.copyOfRange(firstMessage, firstMessage.length / 2, firstMessage.length);
final byte[] leadingHalfOfSecondMessage = Arrays.copyOfRange(secondMessage, 0, secondMessage.length / 2);
final byte[] trailingHalfOfSecondMessage = Arrays.copyOfRange(secondMessage, secondMessage.length / 2, secondMessage.length);
final ObjectMapper mapper = new ObjectMapper();
void runFirstScenario() {
//expectation: remainder = empty array and roots has a single element - parsed firstMessage
final ParsingOutcome result = parse(firstMessage, mapper);
report(result);
}
void runSecondScenario() {
//expectation: remainder = leadingHalfOfFirstMessage and roots is empty
final ParsingOutcome firstResult = parse(leadingHalfOfFirstMessage, mapper);
report(firstResult);
//expectation: remainder = empty array and roots has a single element - parsed firstMessage
final ParsingOutcome secondResult = parse(concat(firstResult.remainder, trailingHalfOfFirstMessage), mapper);
report(secondResult);
}
void runThirdScenario() {
//expectation: remainder = leadingHalfOfSecondMessage and roots has a single element - parsed firstMessage
final ParsingOutcome firstResult = parse(concat(firstMessage, leadingHalfOfSecondMessage), mapper);
report(firstResult);
//expectation: remainder = empty array and roots has a single element - parsed secondMessage
final ParsingOutcome secondResult = parse(concat(firstResult.remainder, trailingHalfOfSecondMessage), mapper);
report(secondResult);
}
void runFourthScenario() {
//expectation: remainder = empty array and roots has two elements - parsed firstMessage, followed by parsed secondMessage
final ParsingOutcome result = parse(concat(firstMessage, secondMessage), mapper);
report(result);
}
static void report(final ParsingOutcome result) {
System.out.printf("Remainder of length %d: %s%n", result.remainder.length, Arrays.toString(result.remainder));
System.out.printf("Total of %d parsed JSON roots: %s%n", result.roots.size(), result.roots);
}
static byte[] concat(final byte[] left, final byte[] right) {
final byte[] union = Arrays.copyOf(left, left.length + right.length);
System.arraycopy(right, 0, union, left.length, right.length);
return union;
}
static ParsingOutcome parse(final byte[] chunk, final ObjectMapper mapper) {
final List<JsonNode> roots = new LinkedList<>();
JsonParser parser;
JsonNode root;
try {
parser = mapper.getFactory().createParser(chunk);
root = parser.readValueAsTree();
} catch (IOException e) {
return new ParsingOutcome(emptyList(), chunk);
}
byte[] remainder = new byte[0];
try {
while(root != null) {
roots.add(root);
remainder = extractRemainder(parser);
root = parser.readValueAsTree();
}
} catch (IOException e) {
//fallthrough
}
return new ParsingOutcome(roots, remainder);
}
static byte[] extractRemainder(final JsonParser parser) {
try {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
parser.releaseBuffered(baos);
return baos.toByteArray();
} catch (IOException e) {
return new byte[0];
}
}
}
To elaborate a bit further, conceptually (at least in my mind), parsing of any streaming data boils down to a simple function which accepts an array of bytes and returns a tuple of (1) a possibly empty list of parsed results and (2) an array of remaining, currently-unparsable bytes. In the snippet above, this tuple is represented by an instance of ParsingOutcome.

Apache Commons CSV : Read Values with comma

I am converting CSV files to a Java Bean. I need to maintain the comma inside a value which is enclosed in "".
Here is my code.
public static PPRCV convertContestToObj(String fileName) throws IOException {
PPRCV pprcvHandler = PPRCVFactory.getPPRCVTable(fileName);
CSVFormat csvFileFormat = CSVFormat.DEFAULT.newFormat(',').withEscape('"');
List<PPRCV> pprcvs = new ArrayList<>();
FileReader fileReader = new FileReader(fileName);
CSVParser csvFileParser = new CSVParser(fileReader, csvFileFormat);
List<CSVRecord> csvRecords = csvFileParser.getRecords();
for (CSVRecord csvRecord : csvRecords) {
pprcvs.add(pprcvHandler.populateDynamicDetails(csvRecord));
}
return pprcvHandler;
}
Sample CSV line:
7080001, XI, ProvinceX, TownX, BRGX, "SHOOL, BRGX", "0054A,0055A,0055B,0055C"
my DTO
private String precintCode;
private String regionName;
private String provinceName;
private String municipalityName;
private String districtName;
private String votingCenter;
private String precint;
My expected output should be
precintCode = "7080001"
regionName = "XI"
provinceName = "ProvinceX"
municipalityName = "TownX"
districtName = "BRGX"
votingCenter = "SCHOOL, BRGX"
precint = "0054A,0055A,0055B,0055C"
However actual output is this
precintCode = "7080001"
regionName = "XI"
provinceName = "ProvinceX"
municipalityName = "TownX"
districtName = "BRGX"
votingCenter = ""SCHOOL"
precint = " , BRGX,"0054A"
You need the withIgnoreSurroundingSpaces() optione here. All other settings could be remain DEFAULT.
final Reader in = new StringReader("7080001, XI, ProvinceX, TownX, BRGX, \"SHOOL, BRGX\", \"0054A,0055A,0055B,0055C\" ");
final CSVFormat csvFileFormat = CSVFormat.DEFAULT.withIgnoreSurroundingSpaces();
for (CSVRecord record: csvFileFormat.parse(in)) {
for (String field: record) {
System.out.println("\"" + field + "\"");
}
System.out.println();
}
The output is
"7080001"
"XI"
"ProvinceX"
"TownX"
"BRGX"
"SHOOL, BRGX"
"0054A,0055A,0055B,0055C"
I was able to do it using the withQuote function from the library.
CSVFormat.EXCEL.newFormat(',').withQuote('"')
Have you already tried using the CSVFormat.DEFAULT constant?-- it's for CSV files adhering to RFC 4180.
The following way worked for me:
CSVFormat.EXCEL.withQuote('"')

How to add List into properties file?

I am converting properties file into xml format like below .
public class XmlPropertiesWriter {
public static void main(String args[]) throws FileNotFoundException, IOException {
//Reading properties files in Java example
Properties props = new Properties();
FileOutputStream fos = new FileOutputStream("C:\\Users\\Desktop\\myxml.xml");
props.setProperty("key1", "test");
props.setProperty("key2", "test1");
//writing properites into properties file from Java
props.storeToXML(fos, "Properties file in xml format generated from Java program");
fos.close();
}
}
This is working fine.But I want to add one ArrayList into this xml file,How can I do this,Any one help me.
You can (un)serialized the list into string representation to store the data into the properties file:
ArrayList<String> list = new ArrayList<>( );
String serialized = list.stream( ).collect( Collectors.joining( "," ) );
String input = "data,data"
List<String> unserialized = Arrays.asList( input.split( "," ) );
With this method, take care to use a seperator which is never contained in your data.
Otherwise, write a xml (or json) file reader/writer to do what you want with support of list element
Depends on what type the ArrayList is. If it's a String type you can do
arrayList.toArray(new String[arrayList.size()]);
If the type is an object you can create a StringBuilder and add all the values seperated by a ; or : so you can split when needed
final StringBuilder builder = new Stringbuilder();
final List<Point> list = new ArrayList<Point>();
list.add(new Point(0, 0));
list.add(new Point(1, 0));
for(final Point p : list) {
builder.append(p.toString()).append(";");
}
properties.setProperty("list", builder.toString());
When you load the properties you can simply do then
final List<Point> list = new ArrayList<Point>();
final String[] points = properties.getProperty("list").split(";");
for(final String p : points) {
final int x = Integer.parseInt(p.substring(0, p.indexOf(","));
final int y = Integer.parseInt(p.substring(p.indexOf(","), p.indexOf(")"));
list.add(new Point(x, y);
}

Jackson - best way writes a java list to a json array

I want to use jackson to convert a ArrayList to a JsonArray.
Event.java : this is the java bean class with two fields "field1", "field2" mapped as JsonProperty.
My goal is:
Convert
ArrayList<Event> list = new ArrayList<Event>();
list.add(new Event("a1","a2"));
list.add(new Event("b1","b2"));
To
[
{"field1":"a1", "field":"a2"},
{"field1":"b1", "field":"b2"}
]
The way I can think of is:
writeListToJsonArray():
public void writeListToJsonArray() throws IOException {
ArrayList<Event> list = new ArrayList<Event>();
list.add(new Event("a1","a2"));
list.add(new Event("b1","b2"));
OutputStream out = new ByteArrayOutputStream();
JsonFactory jfactory = new JsonFactory();
JsonGenerator jGenerator = jfactory.createJsonGenerator(out, JsonEncoding.UTF8);
ObjectMapper mapper = new ObjectMapper();
jGenerator.writeStartArray(); // [
for (Event event : list) {
String e = mapper.writeValueAsString(event);
jGenerator.writeRaw(usage);
// here, big hassles to write a comma to separate json objects, when the last object in the list is reached, no comma
}
jGenerator.writeEndArray(); // ]
jGenerator.close();
System.out.println(out.toString());
}
I am looking for something like:
generator.write(out, list)
this directly convert the list to json array format and then write it to outputstream "out".
even greedier:
generator.write(out, list1)
generator.write(out, list2)
this will just convert/add in the list1, list2 into a single json array. then write it to "out"
This is overly complicated, Jackson handles lists via its writer methods just as well as it handles regular objects. This should work just fine for you, assuming I have not misunderstood your question:
public void writeListToJsonArray() throws IOException {
final List<Event> list = new ArrayList<Event>(2);
list.add(new Event("a1","a2"));
list.add(new Event("b1","b2"));
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(out, list);
final byte[] data = out.toByteArray();
System.out.println(new String(data));
}
I can't find toByteArray() as #atrioom said, so I use StringWriter, please try:
public void writeListToJsonArray() throws IOException {
//your list
final List<Event> list = new ArrayList<Event>(2);
list.add(new Event("a1","a2"));
list.add(new Event("b1","b2"));
final StringWriter sw =new StringWriter();
final ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(sw, list);
System.out.println(sw.toString());//use toString() to convert to JSON
sw.close();
}
Or just use ObjectMapper#writeValueAsString:
final ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(list));
In objectMapper we have writeValueAsString() which accepts object as parameter. We can pass object list as parameter get the string back.
List<Apartment> aptList = new ArrayList<Apartment>();
Apartment aptmt = null;
for(int i=0;i<5;i++){
aptmt= new Apartment();
aptmt.setAptName("Apartment Name : ArrowHead Ranch");
aptmt.setAptNum("3153"+i);
aptmt.setPhase((i+1));
aptmt.setFloorLevel(i+2);
aptList.add(aptmt);
}
mapper.writeValueAsString(aptList)

Categories

Resources