Reputation: 689
i have this type of csv :
metric,value,date
temp_a,622.0,1477895624866
temp_a,-3.0,1477916224866
temp_a,365.0,1477917224866
temp_b,861.0,1477895624866
temp_b,767.0,1477917224866
and i want to use java jackson to convert it to json but not any json; it needs to be like this:
[
{
"metric":"temp_a",
"datapoints":[
[622, 1477895624866],
[-3, 1477916224866],
[365, 1477917224866]
]
},
{
"metric":"temp_b",
"datapoints":[
[861, 1477895624866],
[767, 1477917224866]
]
}
]
where dataponits is an array containing the value and the date in the csv .
i have managed to use the jackson to get this result :
{metric=temp_a, value=622.0, date=1477895624866}
{metric=temp_a, value=-3.0, date=1477916224866}
{metric=temp_a, value=365.0, date=1477917224866}
{metric=temp_b, value=861.0, date=1477895624866}
{metric=temp_b, value=767.0, date=1477917224866}
but it is not what i want and the jackson doc is a bit hard for me to understand and play with , may be this is possible with Pojos or annotations but i can't understand them, i couldn't find how to do a nested json.
if i can do this better this something else then jackson please tell me . thank you for helping.
Upvotes: 2
Views: 3050
Reputation: 38645
You do not have to always deserialise CSV
to a POJO
structure and implement custom serialisers. In this case, you can also:
CSV
to a Map
Map
to a form metric -> [[...], [...]]
Map
to another form of Map
Map
to a JSON
Example code could look like below:
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.MappingIterator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import java.io.File;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class CsvApp {
public static void main(String[] args) throws Exception {
File csvFile = new File("./resource/test.csv").getAbsoluteFile();
CsvMapper csvMapper = CsvMapper.builder().build();
MappingIterator<Map> rows = csvMapper
.readerWithSchemaFor(Map.class)
.with(CsvSchema.emptySchema().withHeader())
.readValues(csvFile);
DataConverter converter = new DataConverter();
List<Map<String, Object>> result = converter.toMetricDataPoints(rows);
ObjectMapper jsonMapper = JsonMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.build();
jsonMapper.writeValue(System.out, result);
}
}
class DataConverter {
public List<Map<String, Object>> toMetricDataPoints(MappingIterator<Map> rows) {
return toStream(rows)
//group by metric -> [value, date]
.collect(Collectors.groupingBy(map -> map.get("metric"),
Collectors.mapping(map -> Arrays.asList(toNumber(map.get("value")), toNumber(map.get("date"))),
Collectors.toList())))
.entrySet().stream()
// convert to Map: metric + datapoints
.map(entry -> {
Map<String, Object> res = new LinkedHashMap<>(4);
res.put("metric", entry.getKey());
res.put("datapoints", entry.getValue());
return res;
}).collect(Collectors.toList());
}
private Stream<Map> toStream(MappingIterator<Map> rowIterator) {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(rowIterator, Spliterator.ORDERED), false);
}
private long toNumber(Object value) {
return new BigDecimal(Objects.toString(value, "0")).longValue();
}
}
Above code prints:
[ {
"metric" : "temp_a",
"datapoints" : [ [ 622, 1477895624866 ], [ -3, 1477916224866 ], [ 365, 1477917224866 ] ]
}, {
"metric" : "temp_b",
"datapoints" : [ [ 861, 1477895624866 ], [ 767, 1477917224866 ] ]
} ]
As you can see, we used only basic Jackson
functionality, rest of manipulation on data we implemented using Java 8
API
.
See also:
Upvotes: 2