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.
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
}
]
}
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.
I have a Java TreeMap frutitas inside a custom object on the server side which I want to send to the frontend.
I use javax.ws and jackson to serialise. The data that I get in the frontend looks like this:
{ "frutitas": {
"entry": [
{
"key": "fruto 1",
"value": "el banano"
},
{
"key": "fruto 2",
"value": "el pineapple"
}
]
}
But I want to get something like this, which is actually how I send the "frutitas" map inside the object that I send to the backend when I want to upload it:
{
"frutitas": {
"fruto 1": "el banano",
"fruto 2": "el pineapple"
}
}
Another option is to use gson.
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
And the class containing the map:
public class FrutitasClass {
private Map<String, String> frutitas;
}
The code below would the conversion:
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(frutitasClassObject);
Out:
{
"frutitas": {
"fruto 1": "el banano",
"fruto 2": "el pineapple"
}
}
You can convert TreeMap to JSONObject as you expected. Here is the sample so that you can get the idea.
JSONObject jsonObject = new JSONObject(yourTreeMap);
If you print jsonObject, Output will be like this.
{"fruto 1":"el banan","fruto 2":"el pineapple"}
JSONObject main = new JSONObject();
main.put("frutitas", jsonObject);
{
"frutitas": {
"fruto 1": "el banano",
"fruto 2": "el pineapple"
}
}
Library Json-Jackson also known as FasterXML is de-facto standard for JSON serialization-deserialization. It works fast and is widely used. Below is a simple class that I wrote for serializing/de-serializing any Object. But in general you need to look at ObjectMapper class to see how it works. Here is Github link to a project. Here are Maven dependencies you may use:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.9.9</version>
</dependency>
My Class Example
package com.bla.json.utils;
import java.io.IOException;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
public class JsonUtil {
private static final ObjectReader objectReader;
private static final ObjectWriter objectWriter;
static {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModules(new JavaTimeModule());
objectMapper.enableDefaultTyping();
objectReader = objectMapper.reader();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectWriter = objectMapper.writer();
}
public static String writeObjectToJsonString(Object object) throws JsonProcessingException {
String jsonData = null;
if (object != null) {
jsonData = objectWriter.writeValueAsString(object);
}
return jsonData;
}
public static <T> T readObjectFromJsonString(String s, Class<T> type) throws IOException {
T data = objectReader.forType(type).readValue(s);
return data;
}
}
I've been writing a Rest service using Jackson to extract the name and sizeFromStorage inside the response value.
I created the below classes in an attempt to do this:
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
#JsonIgnoreProperties(value = {"status", "header"})
public class Vaults {
private List<Object> response;
public Vaults(){
}
public Vaults(List<Object> response){
this.response = response;
}
public List<Object> getResponse(){
return response;
}
public void setResponse(List<Object> response) {
this.response = response;
}
#Override
public String toString() {
return "" + response;
}
}
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.CollectionType;
import java.io.InputStream;
public class JacksonObjectModelTest {
public static void main(String[] args) throws IOException {
String jsonFileName = "/JsonRead/json.json";
List<Vaults> emps = new JacksonObjectModelTest().getVaultList(jsonFileName);
System.out.println(emps.toString());
}
public ArrayList<Vaults> getVaultList(String jsonFileName) throws IOException {
//read json file data to String
InputStream inputStream = getClass().getResourceAsStream(jsonFileName);
//create ObjectMapper instance
ObjectMapper objectMapper = new ObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
//convert json string to object
CollectionType collectionType = objectMapper.getTypeFactory().constructCollectionType(List.class, Vaults.class);
ArrayList<Vaults> emps = objectMapper.readValue(inputStream, collectionType);
return (ArrayList<Vaults>) emps;
}
}
{
"status": "ok",
"header": {
"now": 1491545894581,
"status": "ok",
"requestId": "WOcvJsCoAmoAAESbkBYAAAB5"
},
"response": {
"vault": [
{
"id": 20,
"name": "Apple",
"description": "",
"sizeFromStorage": 95957225298,
"storagePools": [
{
"storagePool": {
"id": 1,
"name": "storage-pool1",
"sizeFromStorage": 95957225298,
"generations": [
{
"generation": {
"sequence": 0
}
}
]
}
}
]
},
{
"id": 21,
"name": "Banana",
"description": "",
"sizeFromStorage": 98957268244,
"storagePools": [
{
"storagePool": {
"id": 2,
"name": "storage-pool1",
"sizeFromStorage": 98957268244,
"generations": [
{
"generation": {
"sequence": 0
}
}
]
}
}
]
},
]
}
}
The output I get from this is:
[[{vaults=[{id=20, name=Apple, description=, sizeFromStorage=95957225298, storagePools=[{storagePool={id=1, name=storage-pool1, sizeFromStorage=5043, estimateUsableTotalLogicalSizeFromStorage=95957225298, generations=[{generation={sequence=0}}]}}]}, {id=20, name=Apple, description=, sizeFromStorage=95957225298, storagePools=[{storagePool={id=1, name=storage-pool1, sizeFromStorage=5043, estimateUsableTotalLogicalSizeFromStorage=95957225298, generations=[{generation={sequence=0}}]}}]}]]
However, what I want to do is the name and sizeFromStorage values. So far I've managed to strip out the first three values. Unfortunately I'm now stuck as I'm not very familiar with Rest services or reading JSON. Is there any way I can delve further into the JSON to get what I need or have I approached this in the wrong way?
Additional info:
Since posting my original question I came up with this(based on something I saw on a different site):
package JsonRead;
import java.io.File;
import java.io.IOException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonTreeModel {
public static void main(String[] args) {
try{
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(new File("JsonRead/json.json"));
JsonNode vaultsNode = root.path("response").path("vault");
/*if(vaultsNode.isArray()){
for(JsonNode node : vaultsNode){
String name = node.path("name").asText();
System.out.println("Array Name: " + name);
}
}*/
for(JsonNode node : vaultsNode){
String name = node.path("name").asText();
String bytesUsed = node.path("sizeFromStorage").asText();
System.out.println("Name: " + name + ", Bytes Used: " + bytesUsed);
}
} catch(IOException e){
System.out.println("IO Exception " + e );
}
}
}
It works for what I want but is there a better way to do this?
Will, thanks for the response. I'll look into your suggestion.
Ok, so i just re-read your question.
Recommend creating yourself a VaultDAO object with a more meaningful custom object type to use for the response collections. Prvoided you use the same variable names (which it looks like are known to you) then it should deserialize for you
I have a simple directed graph from jgrapht and I am trying to serialize it into a JSON file using jackson as follows:
ObjectMapper mapper = new ObjectMapper();
File output = new File("P:\\tree.json");
ObjectWriter objectWriter = mapper.writer().withDefaultPrettyPrinter();
objectWriter.writeValue(output,simpleDirectedGraph);
However I get this error:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class org.jgrapht.graph.AbstractBaseGraph$ArrayListFactory and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: org.jgrapht.graph.SimpleDirectedGraph["edgeSetFactory"])
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:69)
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:32)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:693)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:675)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:130)
at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1387)
at com.fasterxml.jackson.databind.ObjectWriter._configAndWriteValue(ObjectWriter.java:1088)
at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:909)
at ms.fragment.JSONTreeGenerator.main(JSONTreeGenerator.java:45)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
I have seen that there is a GmlExporter but I am interested in json... how can I do that?
You can disable the exception with:
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
The exception you got from Jackson:
JsonMappingException: No serializer found for class org.jgrapht.graph.AbstractBaseGraph$ArrayListFactory
and no properties discovered to create BeanSerializer
(to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)) (through reference chain:
org.jgrapht.graph.SimpleDirectedGraph["edgeSetFactory"])
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:69)
gives a clue on how to solve:
no properties discovered to create BeanSerializer
property SimpleDirectedGraph["edgeSetFactory"] seems empty
Exclude the type of property edgeSetFactory from serialisation:
AbstractBaseGraph$ArrayListFactory
Write a custom Serializer
Usually Jackson would use a StdBeanSerializer to write any non-primitive class to JSON.
Unfortunately this does not work for abstract classes.
So you can write your own JsonSerializer to handle special fields.
You can serialize your Graph to XML and then from XML to JSON :
Serialization to XML : you can use this Library: XStream http://x-stream.github.io it's a small library that will easily allow you to serialize and deserialize to and from XML.
guide to use the Library : http://x-stream.github.io/tutorial.html
After that try to map your XML to JSON using :
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20180813</version>
</dependency>
XML.java is the class you're looking for:
import org.json.JSONObject;
import org.json.XML;
import org.json.JSONException;
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());
}
}
}
Option 1
Newer versions of JGraphT have built-in support for importing/exporting graphs from/to JSON using the jgrapht-io
module.
Here's an example for exporting a graph to JSON:
import org.jgrapht.Graph;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.nio.json.JSONExporter;
import java.net.URI;
import java.net.URISyntaxException;
public class Main {
public static void main(String[] args) throws Exception {
final var jsonExporter = new JSONExporter<URI, DefaultEdge>();
jsonExporter.exportGraph(
newSampleGraph(),
System.out
);
System.out.println("");
}
// Copied from https://jgrapht.org/guide/HelloJGraphT
private static Graph<URI, DefaultEdge> newSampleGraph() throws URISyntaxException {
Graph<URI, DefaultEdge> g = new DefaultDirectedGraph<>(DefaultEdge.class);
URI google = new URI("http://www.google.com");
URI wikipedia = new URI("http://www.wikipedia.org");
URI jgrapht = new URI("http://www.jgrapht.org");
// add the vertices
g.addVertex(google);
g.addVertex(wikipedia);
g.addVertex(jgrapht);
// add edges to create linking structure
g.addEdge(jgrapht, wikipedia);
g.addEdge(google, jgrapht);
g.addEdge(google, wikipedia);
g.addEdge(wikipedia, google);
return g;
}
}
The pom.xml file for reference:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.stackoverflow</groupId>
<artifactId>questions-39438962</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>18</maven.compiler.source>
<maven.compiler.target>18</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.jgrapht</groupId>
<artifactId>jgrapht-core</artifactId>
<version>1.5.1</version>
</dependency>
<dependency>
<groupId>org.jgrapht</groupId>
<artifactId>jgrapht-io</artifactId>
<version>1.5.1</version>
</dependency>
</dependencies>
</project>
Option 2
Implement a custom Jackson serializer for JGraphT graphs, register it with an ObjectMapper, and implement the logic in the serializer.
Here's an example:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import org.jgrapht.Graph;
import org.jgrapht.nio.IntegerIdProvider;
import java.io.IOException;
public class DefaultDirectedGraphSerializer<V, E, T extends Graph<V, E>> extends StdSerializer<T> {
public DefaultDirectedGraphSerializer(Class<T> t) {
super(t);
}
public DefaultDirectedGraphSerializer() {
this(null);
}
#Override
public void serialize(T value, JsonGenerator gen, SerializerProvider provider) throws IOException {
final var idProvider = new IntegerIdProvider<>();
gen.writeStartObject();
gen.writeFieldName("graph");
gen.writeStartObject();
gen.writeFieldName("nodes");
gen.writeStartObject();
for (V v : value.vertexSet()) {
final var id = idProvider.apply(v);
gen.writeFieldName(id);
gen.writeStartObject();
gen.writeStringField("label", v.toString());
gen.writeEndObject();
}
gen.writeEndObject();
gen.writeFieldName("edges");
gen.writeStartArray();
for (E e : value.edgeSet()) {
gen.writeStartObject();
final var source = value.getEdgeSource(e);
final var target = value.getEdgeTarget(e);
gen.writeStringField("source", idProvider.apply(source));
gen.writeStringField("target", idProvider.apply(target));
gen.writeEndObject();
}
gen.writeEndArray();
gen.writeEndObject();
gen.writeEndObject();
}
}
import org.jgrapht.Graph;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.graph.DefaultEdge;
import java.net.URI;
import java.net.URISyntaxException;
public class Graphs {
public static Graph<URI, DefaultEdge> newSampleGraph() throws URISyntaxException {
final var g = new DefaultDirectedGraph<URI, DefaultEdge>(DefaultEdge.class);
URI google = new URI("http://www.google.com");
URI wikipedia = new URI("http://www.wikipedia.org");
URI jgrapht = new URI("http://www.jgrapht.org");
g.addVertex(google);
g.addVertex(wikipedia);
g.addVertex(jgrapht);
g.addEdge(jgrapht, wikipedia);
g.addEdge(google, jgrapht);
g.addEdge(google, wikipedia);
g.addEdge(wikipedia, google);
return g;
}
}
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.jgrapht.graph.DefaultDirectedGraph;
import java.net.URISyntaxException;
import static org.example.Graphs.newSampleGraph;
public class Main {
public static void main(String[] args) throws URISyntaxException, JsonProcessingException {
final var module = new SimpleModule();
module.addSerializer(DefaultDirectedGraph.class, new DefaultDirectedGraphSerializer<>());
final ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
System.out.println(mapper.writeValueAsString(newSampleGraph()));
}
}
This will produce the following JSON document (after pretty printing):
{
"graph": {
"nodes": {
"1": {
"label": "http://www.google.com"
},
"2": {
"label": "http://www.wikipedia.org"
},
"3": {
"label": "http://www.jgrapht.org"
}
},
"edges": [
{
"source": "3",
"target": "2"
},
{
"source": "1",
"target": "3"
},
{
"source": "1",
"target": "2"
},
{
"source": "2",
"target": "1"
}
]
}
}
import org.jgrapht.Graph;
import org.jgrapht.io.JSONExporter;
import org.jgrapht.io.SimpleGraphExporter;
import org.jgrapht.io.SimpleGraphImporter;
import org.jgrapht.io.JSONImporter;
import org.jgrapht.io.JSONExporter;
import org.jgrapht.io.ExportException;
import org.jgrapht.io.ImportException;
// Create a new, empty simple graph
Graph<String, DefaultEdge> graph = new SimpleGraph<>(DefaultEdge.class);
// Add some vertices and edges to the graph
graph.addVertex("A");
graph.addVertex("B");
graph.addVertex("C");
graph.addEdge("A", "B");
graph.addEdge("B", "C");
// Create a new JSONExporter instance
JSONExporter<String, DefaultEdge> exporter = new JSONExporter<>();
// Export the graph to JSON
String json = exporter.toJson(graph);