I have the following json which i need to parse:
{
"resource1": {
"fare": 7511,
"typeoftravel": "domestic",
"vertical": "flight",
"extra": "[{'duration': u'3h 0m', 'arrtime': u'2015-05-05t1930', 'nostops': 0, 'deptime': u'2015-05-05t1630', 'flightno': u'6E478'}]",
"roundtrip": "O",
"destination": "BLR",
"returndate": 0,
"lastupdated": "2015-03-29T10:40:12",
"source": "IXC",
"carrier": "IndiGo",
"date": 20150505,
"class": "E"
},
"resource2": {
"fare": 6320,
"typeoftravel": "domestic",
"vertical": "flight",
"extra": "[{'duration': u'4h 25m', 'arrtime': u'2015-05-06t2210', 'nostops': 0, 'deptime': u'2015-05-06t1745', 'flightno': u'9W7076'}]",
"roundtrip": "O",
"destination": "BLR",
"returndate": 0,
"lastupdated": "2015-03-29T17:08:09",
"source": "IXC",
"carrier": "Jet Airways",
"date": 20150506,
"class": "E"
}
}
I am not able to determine what kind of field "extra" is. It's not a jsonObject or jsonArray. I am able to cast it to jsonPrimitive but now how do i parse this. I am using Gson for parsing.
Assuming you have the following classes:
Resources:
public class Resources {
Resource resource1;
Resource resource2;
}
Resource:
import java.util.List;
public class Resource {
int fare;
String typeoftravel;
String vertical;
String roundtrip;
String destination;
int returndate;
String lastupdated;
String source;
String carrier;
long date;
String clazz;
List<Extra> extra;
}
Extra:
public class Extra {
String duration;
String arrtime;
int nostops;
String deptime;
String flightno;
}
One possible solution, is to register new JsonSerializer to your Gson object, which will
Copy all normal fields, except extra
Extract extra as String
Parse extracted extra again to remove the quotes
Then deserialize it as List using TypeToken
See below code for explanation
ResourceDeserializer
import java.lang.reflect.Type;
import java.util.List;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
public class ResourceDeserializer implements JsonDeserializer<Resource> {
#Override
public Resource deserialize(JsonElement value, Type type,
JsonDeserializationContext context) throws JsonParseException {
final JsonObject resourceJson = value.getAsJsonObject();
final Resource resource = new Resource();
resource.fare = resourceJson.get("fare").getAsInt();
resource.typeoftravel = resourceJson.get("typeoftravel").getAsString();
resource.vertical = resourceJson.get("vertical").getAsString();
resource.roundtrip = resourceJson.get("roundtrip").getAsString();
resource.destination = resourceJson.get("destination").getAsString();
resource.returndate = resourceJson.get("returndate").getAsInt();
resource.lastupdated = resourceJson.get("lastupdated").getAsString();
resource.source = resourceJson.get("source").getAsString();
resource.carrier = resourceJson.get("carrier").getAsString();
resource.date = resourceJson.get("date").getAsLong();
resource.clazz = resourceJson.get("class").getAsString();
Type listType = new TypeToken<List<Extra>>(){}.getType();
String extraStr = resourceJson.get("extra").getAsString();
extraStr = extraStr.replaceAll("u?(\'[^\']+\')", "$1");
JsonElement extraList = new JsonParser().parse(extraStr);
resource.extra = context.deserialize(extraList, listType);
return resource;
}
}
Registering the above deserializer
Gson gson = new GsonBuilder()
.registerTypeAdapter(Resource.class, new ResourceDeserializer())
.create();
Note that, I renamed the attribute class to clazz, because class
is a reserved word!
Related
I have a json file which is having some template already defined say:
{
"ownerContext": 0,
"databaseObjects": [
{
"objectName": "**CT_OPERATOR_ROUTE**",
"dateColumnToFilter": "PRODUCTION_DAY",
"whereCondition": "CREATED_BY IS NULL",
"primaryKeyColumnList": null,
"ignoreColumnList": [
"DAYTIME"
],
"includeAuditColumnList": [
"CREATED_DATE",
"CREATED_BY"
],
"generateAnalysis": true,
"analysisStrategy": "FULL_DETAILS"
},
{
"objectName": "**DT_CT_EQPM_EQPOTHER**",
"dateColumnToFilter": null,
"whereCondition": null,
"primaryKeyColumnList": null,
"ignoreColumnList": [
"DAYTIME"
]
...
}
suppose I have 1000 objects to get into the template then I have to to copy paste every single time,is there any way i can fetch object name from my .txt file and place it as value for object name
My suggestion is:
Read txt file, save object name in a list, maybe List<String>
Create POJO to map json object
Update object name to POJO using for-loop
Example:
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) throws IOException {
List<String> objectNames = getObjectNames();
Context context = updateObjectName(objectNames);
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(context);
System.out.println(json);
}
private static List<String> getObjectNames() throws IOException {
Path path = Path.of("api/src/main/resources/test.txt");
return Files.readAllLines(path);
}
public static Context updateObjectName(List<String> objectNames) {
Context context = new Context();
context.setOwnerContext(0);
List<DatabaseObject> databaseObjects = new ArrayList<>();
for (String name : objectNames) {
DatabaseObject dbObject = new DatabaseObject();
dbObject.setObjectName(name);
databaseObjects.add(dbObject);
}
context.setDatabaseObjects(databaseObjects);
return context;
}
#Data
static class Context {
private int ownerContext;
private List<DatabaseObject> databaseObjects;
}
#Data
static class DatabaseObject {
private String objectName;
private String dateColumnToFilter;
}
}
Result
{
"ownerContext" : 0,
"databaseObjects" : [ {
"objectName" : "Object A",
"dateColumnToFilter" : null
}, {
"objectName" : "Object B",
"dateColumnToFilter" : null
}, {
"objectName" : "Object C",
"dateColumnToFilter" : null
} ]
}
For library:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.4</version>
</dependency>
I am trying to send a list of Javers Changes as JSON via a REST api. While Jackson can handle Java 8 Optionals via loading the respective module it fails to serialize the Change object. When i create a class with Optionals myself the serialization works as expected.
To reproduce one can run the following groovy script:
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module
import org.javers.core.JaversBuilder
import org.javers.repository.jql.QueryBuilder
#Grapes([
#Grab("com.fasterxml.jackson.core:jackson-core:2.8.3"),
#Grab(group='com.fasterxml.jackson.datatype', module='jackson-datatype-jdk8', version='2.8.3'),
#Grab('org.javers:javers-core:2.3.0')]
)
class Test {
def bar
def baz
Test(){
baz = "baz"
bar = "bar"
}
}
def test = new Test()
def javers = JaversBuilder.javers().build()
javers.commit("user", test)
test.bar = "foo"
javers.commit("user", test)
def objectMapper = new ObjectMapper()
objectMapper.registerModule(new Jdk8Module())
println objectMapper.writeValueAsString(javers.findChanges(QueryBuilder.anyDomainObject().build()))
This outputs:
[
{
"commitMetadata": {
"empty": false,
"present": true
},
"propertyName": "bar",
"left": "bar",
"right": "foo",
"affectedGlobalId": {
"typeName": "Test"
},
"affectedLocalId": null,
"affectedObject": {
"empty": true,
"present": false
}
}
]
A custom class gets serialzed as expected:
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module
#Grapes([
#Grab("com.fasterxml.jackson.core:jackson-core:2.8.3"),
#Grab(group='com.fasterxml.jackson.datatype', module='jackson-datatype-jdk8', version='2.8.3')]
)
class Test2 {
def bar
def baz
Test2(){
baz = Optional.of("baz")
bar = "bar"
}
}
def test = new Test2()
def objectMapper = new ObjectMapper()
objectMapper.registerModule(new Jdk8Module())
println objectMapper.writeValueAsString(test)
Output:
{
"bar": "bar",
"baz": "baz"
}
What is special about the Javers Change class, that Jackson refuses to serialze the Optionals?
Jackson doesn't know Javers' Optionals and tries to serialize them as a standard Java Bean which is wrong in this case.
You can fix it by writing a custom serializer for Jackson.
Javers' Optionals exists because replacing them with Java8 Optionals would
brake Java7 clients.
Just had a similar problem again and remembered my question here. In case someone needs a solution. Here is a custom Jackson Serializer for the Javers Optional:
import java.io.IOException;
import org.javers.common.collections.Optional;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
#SuppressWarnings("rawtypes")
public class JaversOptionalSerializer extends StdSerializer<Optional> {
private static final long serialVersionUID = 8330773428393250826L;
public JaversOptionalSerializer() {
super(Optional.class);
}
#Override
public void serialize(final Optional value, final JsonGenerator gen, final SerializerProvider provider) throws IOException {
if(value.isPresent()){
JsonSerializer<Object> serializer = provider.findValueSerializer(value.get().getClass());
serializer.serialize(value.get(), gen, provider);
} else {
gen.writeNull();
}
}
}
I have the following json from our customer:
{
"id": 1234,
"delivery_date": 1234567890,
"actions": [
[ "foo", true],
[ "bar", true]
],
"customer": {
"id": 12345,
"company": "",
"firstname": "John",
"lastname": "Smith",
"action": ["dothis", true]
},
"childs": [ 123abc2132312312,11232432943493]
}
I want to parse the "actions" array as a List< Actions> actionList and the single "action" as Action action.
With
class Action {
String action;
boolean yesno;
}
And the childs Array as List< Child> childs with
class Child{
String id
}
Is that possible without the json keys?
Edit:
Your action class is ok, i mis-read slightly.
Add a complete class:
class Delivery {
Int32 id;
Int32 delivery_date;
list<Action> actions;
Customer customer;
list<Int32> childs;
}
actions will be parsed as a paramtere inside, as will childs.
Then you need to create a Customers class too, which is part of this. (or exclude it, and GSON will ignore it)
This will populate the ints into childs and Actions into actions.
If indeed, childs is alphanumeric, then just change it to String.
You can then access it via,
Delivery delivery = GSON ... etc
var x = delivery.actions; // Actions
var y = delivery.childs; // Childs
I solved it my self with a custom deserializer. Thanks to dzsonni for the hint.
In the Gson root class:
private ArrayList<Action> parcel_actions = new ArrayList<Action>();
the action class
class Action {
String action;
boolean yesno;
}
the deserializer:
public class ActionDeserializer implements JsonDeserializer<ArrayList<Action>> {
#Override
public ArrayList<Action> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
ArrayList<Actions> list = new ArrayList<Action>(){};
if(json.getAsJsonArray().get(0).isJsonPrimitive()){
String action = json.getAsJsonArray().get(0).getAsString();
boolean doIt = json.getAsJsonArray().get(1).getAsBoolean();
list.add(new Action(action, doIt));
}
else {
for(JsonElement element : json.getAsJsonArray()) {
String action = element.getAsJsonArray().get(0).getAsString();
boolean doIt = element.getAsJsonArray().get(1).getAsBoolean();
list.add(new Action(action, doIt));
}
}
return list;
}
}
then just add it to your gson
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(new TypeToken<ArrayList<Action>>(){}.getType(), new ActionsDeserializer());
Gson gson = builder.create();
I'm trying to use MOXy JAXB to unmarshal some JSON/XML in an Apache CFX project. The following request gives me trouble:
{"CC.ConsumerPersistAppDataRequest" : {
"SessionId" : "42",
"AppData" : [
{"AppDatum" : {
"TimeStamp" : 1384548486,
"Value" : "Some kind of String would go here"}},
{"AppDatum" : {
"TimeStamp" : 1384548578,
"Value" : "I hope I am understanding this format correctly!"}},
{"AppDatum" : {
"TimeStamp" : 1384549696,
"Value" : "One more time for the road..."}}],
"MetaDataTags" : ["dumb", "dummy", "data"]}
}
Here, AppData is unmarshalled as a List<AppDatum>, however the list only contains the last element. Notably, the "MetaDataTags" element is correctly unmarshalled as a List<String> of size 3.
Surprisingly, the following request can be submitted with the same result, despite the fact the "AppData" should expect a List<AppDatum> (I can't help but feel this is related):
{"CC.ConsumerPersistAppDataRequest" : {
"SessionId" : "42",
"AppData" : {
"AppDatum" : {
"TimeStamp" : 1384548486,
"Value" : "Some kind of String would go here"
}
}
}}
This XML equivalent (or what I see to be the XML equivalent) is parsed as expected:
<?xml version="1.0" encoding="UTF-8"?>
<cc:ConsumerPersistAppDataRequest xmlns:cc="http://org/alg/ari/pnd/introspect/webservices/consumercomms">
<SessionId>42</SessionId>
<AppData>
<AppDatum>
<TimeStamp>1384548486</TimeStamp>
<Value>Some kind of String would go here</Value>
</AppDatum>
<AppDatum>
<TimeStamp>1384548578</TimeStamp>
<Value>I hope I am understanding this format correctly!</Value>
</AppDatum>
<AppDatum>
<TimeStamp>1384549696</TimeStamp>
<Value>One more time for the road...</Value>
</AppDatum>
</AppData>
<MetaDataTags>dumb dummy data</MetaDataTags>
</cc:ConsumerPersistAppDataRequest>
The JAXB class in question (generated by XJC, comments elided):
package org.alg.ari.pnd.introspect.webservices.consumercomms;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlList;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"sessionId",
"appData",
"metaDataTags"
})
#XmlRootElement(name = "ConsumerPersistAppDataRequest")
public class ConsumerPersistAppDataRequest {
#XmlElement(name = "SessionId", required = true)
protected String sessionId;
#XmlElement(name = "AppData", required = true)
protected ConsumerPersistAppDataRequest.AppData appData;
#XmlList
#XmlElement(name = "MetaDataTags", required = true)
protected List<String> metaDataTags;
public String getSessionId() {
return sessionId;
}
public void setSessionId(String value) {
this.sessionId = value;
}
public ConsumerPersistAppDataRequest.AppData getAppData() {
return appData;
}
public void setAppData(ConsumerPersistAppDataRequest.AppData value) {
this.appData = value;
}
public List<String> getMetaDataTags() {
if (metaDataTags == null) {
metaDataTags = new ArrayList<String>();
}
return this.metaDataTags;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"appDatum"
})
public static class AppData {
#XmlElement(name = "AppDatum", required = true)
protected List<ConsumerPersistAppDataRequest.AppData.AppDatum> appDatum;
public List<ConsumerPersistAppDataRequest.AppData.AppDatum> getAppDatum() {
if (appDatum == null) {
appDatum = new ArrayList<ConsumerPersistAppDataRequest.AppData.AppDatum>();
}
return this.appDatum;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"timeStamp",
"value"
})
public static class AppDatum {
#XmlElement(name = "TimeStamp")
protected long timeStamp;
#XmlElement(name = "Value", required = true)
protected String value;
public long getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(long value) {
this.timeStamp = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
}
and my MoxyJsonProvider bean
<bean id="moxy-json-provider"
class="org.eclipse.persistence.jaxb.rs.MOXyJsonProvider">
<property name="attributePrefix" value="#" />
<property name="formattedOutput" value="true" /> <!-- set to false for production! -->
<property name="includeRoot" value="true" />
<property name="marshalEmptyCollections" value="false" />
<property name="valueWrapper" value="$" />
<property name="namespacePrefixMapper" ref="json-ns-mapper" />
</bean>
Any idea what could be causing this issue? Is this a bug in the 2.5.1 Release of MOXy? Or is there a "switch" somewhere that I need to set?
Based on your mapping, the folloiwng would be the expected JSON document:
{
"CC.ConsumerPersistAppDataRequest" : {
"SessionId" : "42",
"AppData" : {
"AppDatum" : [ {
"TimeStamp" : 1384548486,
"Value" : "Some kind of String would go here"
}, {
"TimeStamp" : 1384548578,
"Value" : "I hope I am understanding this format correctly!"
}, {
"TimeStamp" : 1384549696,
"Value" : "One more time for the road..."
} ]
},
"MetaDataTags" : "dumb dummy data"
}
}
The following is the standalone example I wrote based on yours to read in the XML and output the corresponding JSON:
import java.io.File;
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.MarshallerProperties;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ConsumerPersistAppDataRequest.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum20056524/input.xml");
ConsumerPersistAppDataRequest result = (ConsumerPersistAppDataRequest) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
marshaller.setProperty(MarshallerProperties.JSON_ATTRIBUTE_PREFIX, "#");
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, true);
marshaller.setProperty(MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, false);
marshaller.setProperty(MarshallerProperties.JSON_VALUE_WRAPPER, "$");
Map<String, String> jsonNsMapper = new HashMap<String, String>(1);
jsonNsMapper.put("http://org/alg/ari/pnd/introspect/webservices/consumercomms", "CC");
marshaller.setProperty(MarshallerProperties.NAMESPACE_PREFIX_MAPPER, jsonNsMapper);
marshaller.marshal(result, System.out);
}
}
I have a larga data JSON which I want to pass to the backend to be parsed there to become into java objects.
To make this I'm using the JSON.stringify function but inside the JSON there is an array attribute that the JSON.stringify is enclosing between quotes ("), so when Gson find it (the way I'm using at the backend to decode the string into objects), it throws an error because this is not an array inside a JSON string representation, but an string attibute inside a JSON string representation.
This is an example of the string generated with JSON.stringify:
{"id":0, "array": "[{\"id\":0, \"cod\": \"XXX\"}, {\"id\":0, \"cod\": \"XXX\"}]"}
The array attribute cannot be converted by Gson because is not an array.
Can anybody help me with this issue?
Thanks a lot.
I'd likely prefer to fix the generated JSON, but if that's not possible or otherwise preferable, it looks like you'll simply need to deserialize part of the JSON twice. This could be accomplished with a custom deserializer as follows.
import java.lang.reflect.Type;
import java.util.Arrays;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
public class GsonFoo
{
public static void main(String[] args)
{
// With the generated JSON corrected:
// {"id":42, "array": [{"id":1, "cod": "aaa"}, {"id":2, "cod": "bbb"}]}
String jsonInput = "{\"id\":42, \"array\": [{\"id\":1, \"cod\": \"aaa\"}, {\"id\":2, \"cod\": \"bbb\"}]}";
Gson gson = new Gson();
Bar bar1 = gson.fromJson(jsonInput, Bar.class);
System.out.println(bar1);
// Bar: id=42, array=[Thing: id=1, cod=aaa, Thing: id=2, cod=bbb]
// -------------------------
// With the funky JSON:
// {"id":42, "array": "[{\"id\":1, \"cod\": \"aaa\"}, {\"id\":2, \"cod\": \"bbb\"}]"}
String funkyJsonInput = "{\"id\":42, \"array\": \"[{\\\"id\\\":1, \\\"cod\\\": \\\"aaa\\\"}, {\\\"id\\\":2, \\\"cod\\\": \\\"bbb\\\"}]\"}";
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Thing[].class, new FunkyThingArrayDeserializer());
gson = gsonBuilder.create();
Bar bar2 = gson.fromJson(funkyJsonInput, Bar.class);
System.out.println(bar2);
// Bar: id=42, array=[Thing: id=1, cod=aaa, Thing: id=2, cod=bbb]
}
}
class FunkyThingArrayDeserializer implements JsonDeserializer<Thing[]>
{
#Override
public Thing[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException
{
String actualJson = context.deserialize(json, String.class);
return new Gson().fromJson(actualJson, Thing[].class);
}
}
class Bar
{
int id;
Thing[] array;
#Override
public String toString()
{
return String.format("Bar: id=%d, array=%s", id, Arrays.toString(array));
}
}
class Thing
{
int id;
String cod;
#Override
public String toString()
{
return String.format("Thing: id=%d, cod=%s", id, cod);
}
}
You need to write this code before invoke JSON.stringify
if(window.Prototype) {
delete Object.prototype.toJSON;
delete Array.prototype.toJSON;
delete Hash.prototype.toJSON;
delete String.prototype.toJSON;
}