I,m given a problem to convert JSON file to excel file, in short to convert JSON data to excel data. Tried mapping JSON keys and values but can't do it.
Tried mapping JSON keys and values but can't do it. I have already used apache POI api.
public class jsontoexcel {
public static void main(String[] args) throws IOException,JSONException {
jsontoexcel json4=new jsontoexcel();
JSONObject json=json4.ReadJson();
JSONArray array =new JSONArray();
JSONObject rowjson=json.getJSONArray("rows").getJSONObject(0);
XSSFWorkbook workbook=new XSSFWorkbook();
XSSFSheet sheet=workbook.createSheet("Company Details");
int len=rowjson.length();
String[] RowArr=new String[len];
Iterator<String> keys = rowjson.keys();
int i=0;
while(keys.hasNext())
{
RowArr[i]=keys.next();
System.out.print("key:"+keys);
i++;
}
List<String> slist= new ArrayList<String>();
slist=json.get(rowjson.toString(keys));
FileOutputStream out=new FileOutputStream(new File("C:\\code\\eclipse\\jsontoexcel\\src\\output.xlsx"));
createHeaderRow(sheet, RowArr);
workbook.write(out);
out.close();
// Map<String,Object> map=new Map<String,Object>();
}
public static void createHeaderRow(XSSFSheet sheet, String[] RowArr)
{
Row row=sheet.createRow(0);
for(int i=0;i<RowArr.length-1;i++)
{
Cell cellTitle=row.createCell(i+1);
String cellVal=RowArr[i];
System.out.print("Cell data" + cellVal);
}
}
}
I expect the output to be stored in an excel file. The headers are getting printed but not the values.
Do not generate Excel file until you really have to. In case, you want to generate data without any specific formatting, charts, macros, etc. just generate CSV file with pure data. To read JSON and generate CSV you can use Jackson library which supports these two data formats. Just assume your JSON looks like below:
{
"rows": [
{
"id": 1,
"name": "Vika",
"age": 27
},
{
"id": 2,
"name": "Mike",
"age": 28
}
]
}
You, need to create POJO model which fits to that structure, deserialise JSON to objects and serialise objects to CSV format. Example solution, could look like below:
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SequenceWriter;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import java.io.File;
import java.util.List;
public class JsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
ObjectMapper jsonMapper = new ObjectMapper();
Response response = jsonMapper.readValue(jsonFile, Response.class);
CsvMapper csvMapper = new CsvMapper();
CsvSchema schema = csvMapper.schemaFor(Item.class).withHeader();
SequenceWriter sequenceWriter = csvMapper.writer(schema).writeValues(System.out);
sequenceWriter.writeAll(response.getRows());
}
}
class Response {
private List<Item> rows;
// getters, setters
}
#JsonPropertyOrder({"id", "name", "age"})
class Item {
private int id;
private String name;
private int age;
// getters, setters
}
Above code prints:
id,name,age
1,Vika,27
2,Mike,28
See also:
jackson-dataformats-text
Intro to the Jackson ObjectMapper
Related
I am trying to create a JSON using the Jackson Streaming API. I know how to create an array of elements in JSON using Jackson as we have plenty of examples related to it. But I am a bit confused about how to create an array of Objects using it.
Following is the JSON structure that I would like to obtain at the end:
{
"name" : "Batman",
"year" : 2008,
"writers":[
{
"name" : "Nolan",
"age" : 49
},
{
"name" : "Johnathan",
"age" : 35
}
]
}
Following is the code I have:
import org.json.JSONObject;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
public class HelloWorld {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
ByteArrayOutputStream jsonStream = new ByteArrayOutputStream();
JsonGenerator jsonGenerator = mapper.getFactory().createGenerator(jsonStream, JsonEncoding.UTF8);
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("name", "Batman");
jsonGenerator.writeNumberField("year", 2008);
jsonGenerator.writeFieldName("writers");
jsonGenerator.writeStartArray();
// How to to create here objects and add it to the "writers"
// Should I create another JsonGenerator and create objects usign it?
jsonGenerator.writeEndArray();
jsonGenerator.writeEndObject();
jsonGenerator.close();
String jsonData = new String(jsonStream.toByteArray(), "UTF-8");
JSONObject json = new JSONObject(jsonData);
System.out.println(json.toString(4));
}
}
Can someone please guide me on how to create the objects and add them to the array one by one? I am unable to find such an example so posting here.
I would just create a Map to store the data. For the writers, you can call List.of to create an in-line List.
import java.io.*;
import java.util.*;
import com.fasterxml.jackson.databind.*;
public class MovieDataWriter {
public static void main(String[] args) {
Map<String, Object> movieData = createMap(
"name", "Batman",
"year", 2008,
"writers", List.of(
createMap(
"name", "Nolan",
"age", 49
),
createMap(
"name", "Johnathan",
"age", 35
)
)
);
writeToFile(movieData, "target/batman.json");
}
private static void writeToFile(Map<String, Object> data, String filename) {
ObjectMapper mapper = new ObjectMapper();
ObjectWriter writer = mapper.writerWithDefaultPrettyPrinter();
try {
writer.writeValue(new File(filename), data);
} catch (IOException e) {
e.printStackTrace();
}
}
private static Map<String, Object> createMap(Object ...args) {
Map<String, Object> pairs = new LinkedHashMap<>();
for (int i = 0; i < args.length; i += 2) {
pairs.put(String.valueOf(args[i]), args[i + 1]);
}
return pairs;
}
}
Dependencies
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.1</version>
</dependency>
batman.json
{
"name" : "Batman",
"year" : 2008,
"writers" : [ {
"name" : "Nolan",
"age" : 49
}, {
"name" : "Johnathan",
"age" : 35
} ]
}
After trying a few things I was able to get it. Basically, I had to do the same thing which I was asked in the question. I am not sure why it did not work the first time maybe I missed something. Anyways here is how you can add objects into the array using the Jackson Streaming API. Posting this as it can be beneficial to someone else in the future.
I am creating an array writers in this case and adding the objects into it using the same jsonGenerator.
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.json.JSONObject;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
public class HelloWorld {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
ByteArrayOutputStream jsonStream = new ByteArrayOutputStream();
JsonGenerator jsonGenerator = mapper.getFactory().createGenerator(jsonStream, JsonEncoding.UTF8);
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("name", "Batman");
jsonGenerator.writeNumberField("year", 2008);
jsonGenerator.writeFieldName("writers");
jsonGenerator.writeStartArray();
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("name", "Nolan");
jsonGenerator.writeNumberField("age", 45);
jsonGenerator.writeEndObject();
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("name", "Johanathan");
jsonGenerator.writeNumberField("age", 35);
jsonGenerator.writeEndObject();
jsonGenerator.writeEndArray();
jsonGenerator.writeEndObject();
jsonGenerator.close();
String jsonData = new String(jsonStream.toByteArray(), "UTF-8");
JSONObject json = new JSONObject(jsonData);
System.out.println(json.toString(4));
}
}
You will get the output something like this:
{
"year": 2008,
"name": "Batman",
"writers": [
{
"name": "Nolan",
"age": 45
},
{
"name": "Johanathan",
"age": 35
}
]
}
I try to simply read the json file, followed by the creation of the class object, but it does not work, BufferedReader does not even read the file, although in the examples it is directly passed to GSON, the dependency is added to the pom file.
json
{
"student": [{
"name": "Mark",
"surname": "Ivanov",
}, {
"name": "Peter",
"surname": "Ivanov",
}]
}
Created classes
public class POJOStudents
{
String name;
String surname;
}
public class GeneralStudents {
List<POJOStudents> student;
}
import java.io.BufferedReader;
import java.io.FileReader;
import com.google.gson.Gson;
public class Main
{
public static void main(String[] args) throws Exception
{
Gson gson = new Gson();
BufferedReader br = new BufferedReader(new FileReader("D:\\student.json"));
GeneralStudents student = gson.fromJson(br, GeneralStudents.class);
}
}
As theBittor stated: You have to remove the trailing commas after surname. Then the main can read your json.
By the way your readers should be surrounded by try-finally or use the Autoclose feature of Java with
try (BufferedReader br = new BufferedReader(new FileReader("D:\\student.json"))) {
GeneralStudents student = gson.fromJson(br, GeneralStudents.class);
}
I'm creating a Spring application on backend and my main goal is to manage properties (add/update/delete) in *.properties file. I want to convert this file to JSON and then manipulate it from UI application.
Is there any possibility to convert structure like this:
a.x=1
a.y=2
b.z=3
To JSON like this:
{
"a": {
"x": 1,
"y": 2
},
"b": {
"z": 3
}
}
I found solution to use GSON library, but it creates for me flat structure, not hierarchical, code I used:
Properties props = new Properties();
try (FileInputStream in = new FileInputStream(classPathResource.getFile())) {
props.load(in);
}
String json = new GsonBuilder().enableComplexMapKeySerialization().create().toJson(props);
Is here someone who was facing same problem and maybe found a working project for this? Maybe GSON library can do that?
This solution does involve loads of work, but you will get what you want to achieve using the below code, basically, the idea is to split the key based on the single dot and then create a JsonObject if the same first key is found.
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Properties;
import org.json.JSONObject;
import com.fasterxml.jackson.annotation.JsonIgnore;
public class SOTest {
public static void main(String[] args) throws IOException {
JSONObject jsonObject = new JSONObject();
FileReader fileReader = new FileReader(new File("C:\\Usrc\\main\\java\\Sample.properties"));
Properties properties = new Properties();
properties.load(fileReader);
Iterator<Entry<Object, Object>> iterator = properties.entrySet().iterator();
while (iterator.hasNext()) {
Entry<Object, Object> entry = iterator.next();
String value = (String) entry.getKey();
String[] values = value.split("\\.");
JSONObject opt = jsonObject.optJSONObject(values[0]);
if(opt!=null) {
opt.put(values[1],entry.getValue());
}else {
JSONObject object = new JSONObject();
object.put(values[1], entry.getValue());
jsonObject.put(values[0], object);
}
}
System.out.println(jsonObject.toString());
}
}
Output
{"a":{"x":"1","y":"3"},"b":{"z":"10"}}
I'm trying to parse a CSV file using jackson-dataformat-csv and I want to map the numeric column to the Number java type.
CsvSchema schema = CsvSchema.builder().setUseHeader(true)
.addColumn("firstName", CsvSchema.ColumnType.STRING)
.addColumn("lastName", CsvSchema.ColumnType.STRING)
.addColumn("age", CsvSchema.ColumnType.NUMBER)
.build();
CsvMapper csvMapper = new CsvMapper();
MappingIterator<Map<String, Object>> mappingIterator = csvMapper
.readerFor(Map.class)
.with(schema)
.readValues(is);
while (mappingIterator.hasNext()) {
Map<String, Object> entryMap = mappingIterator.next();
Number age = (Number) entryMap.get("age");
}
I'm expecting entryMap.get("age") should be a Number, but I get String instead.
My CSV file:
firstName,lastName,age
John,Doe,21
Error,Name,-10
I know that CsvSchema works fine with POJOs, but I need to process arbitrary CSV schemas, so I can't create a new java class for every case.
Any way to parse CSV into a typed Map or Array?
Right now it is not possible to configure Map deserialisation using CsvSchema. Process uses com.fasterxml.jackson.databind.deser.std.MapDeserializer which right now does not check schema. We could write custom Map deserialiser. There is a question on GitHub: CsvMapper does not respect CsvSchema.ColumnType when using #JsonAnySetter where cowtowncoder answered:
At this point schema type is not used much for anything, but I agree
it should.
EDIT
I decided to take a look closer what we can do with that fact that com.fasterxml.jackson.databind.deser.std.MapDeserializer is used behind the scene. Implementing custom Map deserialiser which will take care about types would be tricky to implement and register but we can use knowledge about ValueInstantiator. Let's define new Map type which knows what to do with ColumnType info:
class CsvMap extends HashMap<String, Object> {
private final CsvSchema schema;
private final NumberFormat numberFormat = NumberFormat.getInstance();
public CsvMap(CsvSchema schema) {
this.schema = schema;
}
#Override
public Object put(String key, Object value) {
value = convertIfNeeded(key, value);
return super.put(key, value);
}
private Object convertIfNeeded(String key, Object value) {
CsvSchema.Column column = schema.column(key);
if (column.getType() == CsvSchema.ColumnType.NUMBER) {
try {
return numberFormat.parse(value.toString());
} catch (ParseException e) {
// leave it as it is
}
}
return value;
}
}
For new type without no-arg constructor we should create new ValueInstantiator:
class CsvMapInstantiator extends ValueInstantiator.Base {
private final CsvSchema schema;
public CsvMapInstantiator(CsvSchema schema) {
super(CsvMap.class);
this.schema = schema;
}
#Override
public Object createUsingDefault(DeserializationContext ctxt) {
return new CsvMap(schema);
}
#Override
public boolean canCreateUsingDefault() {
return true;
}
}
Example usage:
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.MappingIterator;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import java.io.File;
import java.io.IOException;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.HashMap;
public class CsvApp {
public static void main(String[] args) throws IOException {
File csvFile = new File("./resource/test.csv").getAbsoluteFile();
CsvSchema schema = CsvSchema.builder()
.addColumn("firstName", CsvSchema.ColumnType.STRING)
.addColumn("lastName", CsvSchema.ColumnType.STRING)
.addColumn("age", CsvSchema.ColumnType.NUMBER)
.build().withHeader();
// Create schema aware map module
SimpleModule csvMapModule = new SimpleModule();
csvMapModule.addValueInstantiator(CsvMap.class, new CsvMapInstantiator(schema));
// register map
CsvMapper csvMapper = new CsvMapper();
csvMapper.registerModule(csvMapModule);
// get reader for CsvMap + schema
ObjectReader objectReaderWithSchema = csvMapper
.readerWithSchemaFor(CsvMap.class)
.with(schema);
MappingIterator<CsvMap> mappingIterator = objectReaderWithSchema.readValues(csvFile);
while (mappingIterator.hasNext()) {
CsvMap entryMap = mappingIterator.next();
Number age = (Number) entryMap.get("age");
System.out.println(age + " (" + age.getClass() + ")");
}
}
}
Above code for below CSV payload:
firstName,lastName,age
John,Doe,21
Error,Name,-10.1
prints:
21 (class java.lang.Long)
-10.1 (class java.lang.Double)
It looks like a hack but I wanted to show this possibility.
You can use univocity-parsers for this sort of thing. It's faster and way more flexible:
CsvParserSettingssettings = new CsvParserSettings(); //configure the parser if needed
CsvParser parser = new CsvParser(settings);
for (Record record : parser.iterateRecords(is)) {
Short age = record.getShort("age");
}
To get a typed map, tell the parser what is the type of the columns you are working with:
parser.getRecordMetadata().setTypeOfColumns(Short.class, "age" /*, and other column names*/);
//to get 0 instead of nulls when the field is empty in the file:
parser.getRecordMetadata().setDefaultValueOfColumns("0", "age", /*, and other column names*/);
// then parse
for (Record record : parser.iterateRecords(is)) {
Map<String,Object> map = record.toFieldMap();
}
Hope this helps
Disclaimer: I'm the author of this library. It's open source and free (Apache 2.0 license)
Wanted to covert an xml String to Json and I am doing it as below.
XML which has to be converted
<Item>
<Property name="Description" value="Description 1"/>
<Property name="EffDate" value="01/05/2017"/>
<Property name="ExpDate" value="12/31/9999"/>
<Property name="Status" value="Launched"/>
</Item>
I have created a Class for the xml as below.
public class Context {
#XmlElement(name = "Item")
private List<Item> offer;
}
public class Item {
#XmlElement(name = "Property")
private List<Property> properties;
}
public class Property {
#XmlAttribute
private String name;
#XmlAttribute
private String value;
}
I am using Gson libraries to convert this Java object to Json - g.toJson.
Comverted JSON -
"offer": [{
"properties": [{
"name": "Description",
"value": "Description 1"
},
{
"name": "EffDate",
"value": "01/05/2017"
},
{
"name": "ExpDate",
"value": "12/31/9999"
},
{
"name": "Status",
"value": "Launched"
}]
}]
But we wanted to convert the JSON as below -
"offer": [{
"Description" : "Description 1",
"EffDate":"01/05/2017",
"ExpDate": "12/31/9999",
"Status": "Launched"
}]
Is there a way to convert the properties name and value as Item class properties.?
Try using this link: https://github.com/stleary/JSON-java This is a JSON Helper class that can convert XML to JSON for example:
public class Main {
public static int PRETTY_PRINT_INDENT_FACTOR = 4;
public static String TEST_XML_STRING =
"<?xml version=\"1.0\" ?><test attrib=\"moretest\">Turn this to JSON</test>";
public static void main(String[] args) {
try {
JSONObject xmlJSONObj = XML.toJSONObject(TEST_XML_STRING);
String jsonPrettyPrintString = xmlJSONObj.toString(PRETTY_PRINT_INDENT_FACTOR);
System.out.println(jsonPrettyPrintString);
} catch (JSONException je) {
System.out.println(je.toString());
}
}
}
Hope this helps :)
It is possible using FasterXML library. where you can write your custom logic for generating XML and JSON. By overriding serialize of JsonSerializer class.
Need to write Serializer like :
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class ContextSerializer extends JsonSerializer<Context> {
#Override
public void serialize(Context t, JsonGenerator jg, SerializerProvider sp) throws IOException, JsonProcessingException {
jg.writeStartObject();
jg.writeArrayFieldStart("offer");
for (Item i : t.offer) {
jg.writeStartObject();
for (Property property : i.properties) {
jg.writeStringField(property.name, property.value);
}
jg.writeEndObject();
}
jg.writeEndArray();
jg.writeEndObject();
}
}
For convert:
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBException;
public class Main {
public static void main(String[] args) throws JAXBException, JsonProcessingException {
Context c = new Context();
List<Item> offer = new ArrayList<>();
Item pr = new Item();
pr.properties = new ArrayList<>();
Property p = new Property();
p.name = "asdf";
p.value = "va1";
pr.properties.add(p);
p = new Property();
p.name = "asdf1";
p.value = "va11";
pr.properties.add(p);
offer.add(pr);
c.offer = offer;
try {
SimpleModule module = new SimpleModule();
module.addSerializer(Context.class, new ContextSerializer());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);
objectMapper.setSerializationInclusion(Include.NON_DEFAULT);
String json = objectMapper.writeValueAsString(c);
System.out.println(json);
} catch (Exception e) {
System.out.println(""+e);
}
}
}
O/P JSON : (Provided O/P JSON is wrong in your question if you give the name to the list("offer") then it always inside object link)
{
"offer": [{
"asdf": "va1",
"asdf1": "va11"
}
]
}
Maven Dependency for package is:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0.pr3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0.pr3</version>
</dependency>
If you are using Java 8 or later, you should check out my open source library: unXml. unXml basically maps from Xpaths to Json-attributes.
It's available on Maven Central.
Example
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.nerdforge.unxml.factory.ParsingFactory;
import com.nerdforge.unxml.parsers.Parser;
import org.w3c.dom.Document;
public class Parser {
public ObjectNode parseXml(String xml){
Parsing parsing = ParsingFactory.getInstance().create();
Document document = parsing.xml().document(xml);
Parser<ObjectNode> parser = parsing.obj("/")
.attribute("offer", parsing.arr("/Item")
.attribute("Description", "Property[#name='Description']/#value")
.attribute("EffDate", "Property[#name='EffDate']/#value")
.attribute("ExpDate", "Property[#name='ExpDate']/#value")
.attribute("Status", "Property[#name='Status']/#value")
)
.build();
ObjectNode result = parser.apply(document);
return result;
}
}
It will return a Jackson ObjectNode, with the following json:
{
"offer": [
{
"Status": "Launched",
"Description": "Description 1",
"ExpDate": "12/31/9999",
"EffDate": "01/05/2017"
}
]
}
You may convert xml to a map, modify it and then convert to a json. Underscore-java library has static methods U.fromXml(xml) and U.toJson(json). I am the maintainer of the project.