I am trying to apply Lifecycle Configurations on S3 bucket. Trying to apply using following JSON:
[{
"id": "tmpdelete",
"status": "Enabled",
"filter": {
"predicate": {
"prefix": "tmp"
}
},
"transitions": [{
"days": "1",
"storageClass": "GLACIER"
}],
"noncurrentVersionTransitions": [{
"days": "1",
"storageClass": "GLACIER"
}],
"expirationInDays": "2",
"noncurrentVersionExpirationInDays": "2",
"expiredObjectDeleteMarker": "true"
}]
When i am trying to map it with Rule[].class it is not working. I am using following code:
String json = above_json;
Rule[] rules = null;
Gson gson = new GsonBuilder().serializeNulls().excludeFieldsWithModifiers(Modifier.FINAL,
Modifier.TRANSIENT, Modifier.STATIC, Modifier.ABSTRACT).create();
rules = gson.fromJson(json, Rule[].class);
try {
amazonS3.setBucketLifecycleConfiguration(bucketName, new BucketLifecycleConfiguration().withRules(rules));
} catch (Exception e) {
throw e;
}
It throws error saying Failed to invoke public com.amazonaws.services.s3.model.lifecycle.LifecycleFilterPredicate() with no args. LifecycleFilterPredicate is an abstract class which implements Serializable and it doesn't have no-args contructor. How to solve this problem.?
Ok, I think I found your problem: when GSON tries to construct the objects from that json string into an actual object (or, in this case, a list of objects), the process fails because when it gets to the filter.predicate bit, it probably tries to do something like this:
LifecycleFilterPredicate predicate = new LifecycleFilterPredicate();
predicate.setPrefix("tmp");
Which doesn't work because LifecycleFilterPredicate doesn't have a public constructor without any arguments, as you've stated.
I think that, unfortunately, your only solution is to parse the JSON in a different way.
UPDATE
You'll need to make use of a GSON TypeAdapter as follows:
class LifecycleFilterPredicateAdapter extends TypeAdapter<LifecycleFilterPredicate>
{
#Override
public LifecycleFilterPredicate read(JsonReader reader)
throws IOException
{
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
reader.beginObject();
if(!"prefix".equals(reader.nextName()))
{
return null;
}
String prefix = reader.nextString();
LifecyclePrefixPredicate predicate = new LifecyclePrefixPredicate(prefix);
reader.endObject();
return predicate;
}
#Override
public void write(JsonWriter writer, LifecycleFilterPredicate predicate)
throws IOException
{
//nothing here
}
}
...
Gson gson = new GsonBuilder().serializeNulls().excludeFieldsWithModifiers(Modifier.FINAL,
Modifier.TRANSIENT, Modifier.STATIC, Modifier.ABSTRACT)
.registerTypeAdapter(LifecycleFilterPredicate.class, new LifecycleFilterPredicateAdapter()).create();
I've tried it locally and don't get the exception anymore :)
I tried this and it worked for me
public class RuleInstanceCreator implements InstanceCreator<LifecycleFilterPredicate> {
#Override
public LifecycleFilterPredicate createInstance(Type type) {
return new LifecycleFilterPredicate() {
private static final long serialVersionUID = 1L;
#Override
public void accept(LifecyclePredicateVisitor lifecyclePredicateVisitor) {
}
};
}
}
Gson gson = new GsonBuilder()
.registerTypeAdapter(LifecycleFilterPredicate.class, new LifecycleFilterPredicateAdapter()).create();
rules = gson.fromJson(json, Rule[].class);
Related
I am looking to serialize my class using gson but I would like to omit the hashmap name. Is this possible with gson?
I have tried writing my own TypeAdapter but the map name is still written as the parent object.
I have a class which looks like
public class myClass {
#Expose
public Long timestamp;
#Expose
public String id;
#Expose
public HashMap<String, someOtherClass> myMap = new HashMap<>();
#Override
public String toString() {
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
return gson.toJson(this);
}
}
current output :
{
"timestamp": 1517245340000,
"id": "01",
"myMap": {
"mapKey1": {
"otherClassId": "100", // works as expected
}
"mapKey2": {
"otherClassId": "101", // works as expected
}
}
}
What I am hoping to get :
{
"timestamp": 1517245340000,
"id": "01",
"mapKey1": {
"otherClassId": "100", // works as expected
},
"mapKey2": {
"otherClassId": "100", // works as expected
}
}
Write your own TypeAdapter. See javadoc for example.
Specify it with #JsonAdapter annotation, or register it with GsonBuilder.
#JsonAdapter(MyClassAdapter.class)
public class MyClass {
public Long timestamp;
public String id;
public HashMap<String, SomeOtherClass> myMap = new HashMap<>();
}
public class MyClassAdapter extends TypeAdapter<MyClass> {
#Override public void write(JsonWriter out, MyClass myClass) throws IOException {
// implement the write method
}
#Override public MyClass read(JsonReader in) throws IOException {
// implement the read method
return ...;
}
}
I have a JSON structure which I want to parse using retrofit 2 (#Expose). Below I have mentioned the JSON. Need help to parse it using dynamic annotations.
{
"status": 1,
"message": "success",
"data" : [
{
"type": 1,
"heading": "",
"description": "",
"created_on": 141123213,
"author_id": 123,
"author_name": "some name",
"author_pic": "some_pic",
"read_time": "3.1 min",
"post_pic_url": "",
"post_web_url": "",
"isLiked": false,
"isSaved": false,
"totalLikes": 12
},
{
"type": 2,
"author_id": 123,
"author_name": "some name",
"author_pic": "some pic",
"author_about": "",
"tags":[
"travel", "weekends"
],
"isFollowing": false
},
{
"type": 3,
"poll_name": "Some name",
"poll_options": [
"opt1", "opt2", "opt3"
],
"author_id": 123,
"author_name": "some name",
"author_pic": "some pic",
"isLiked": true,
"isFollowing": false
},
{
"type": 4,
"ad_url": "url",
"ad_pic": "pic"
},
{
"type": 5,
"tags": [
"tag1", "tag2", "tag3"
]
}
]
}
I have updated the JSON structure with all 5 types.
1 Use Retrofit convert
example GSON convert
2 Add com.squareup.retrofit2:converter-gson in gradle file
3 Add converter factory in Retrofit object
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Ws_Url)
.addConverterFactory(GsonConverterFactory.create())
.client(clientBuilder.build())
.build();
4 Create Model class for Your response
Use below link to generate model class
http://www.jsonschema2pojo.org/
Retrofit does not do serialization and deserialization, but Gson does.
You might want to use RuntimeTypeAdapterFactory from the Google Gson extras package.
It's not published at artifact repositories, and you can simply copy the code to your project.
If type adapters are somewhat complex (as they work with JSON streams), you might find JsonDeserializer<T> easier to use and probably maintain (they work with JSON trees consuming some more memory, but it's the only way to go here anyway).
Define your mappings similar to:
// There might be no the common root, and target lists might be parameterized with Object, but it's up to you
abstract class Element {
final int type = Integer.valueOf(0);
// Since the number of types is really finite, we can define all known types in one place
private Element() {
}
static final class Type1Element
extends Element {
// the rest of properties go here
// Gson does not need constructors, neither we do (at least public ones)
private Type1Element() {
}
}
static final class Type2Element
extends Element {
// the rest of properties go here
private Type2Element() {
}
}
}
final class Response<T> {
final int status = Integer.valueOf(0);
final String message = null;
final T data = null;
}
Now the deserializer itself:
final class ElementJsonDeserializer
implements JsonDeserializer<Element> {
private static final JsonDeserializer<Element> elementJsonDeserializer = new ElementJsonDeserializer();
private ElementJsonDeserializer() {
}
// The deserializer is essentially a singleton, but we hide away this fact making sure that only 1 instance exists
static JsonDeserializer<Element> getElementJsonDeserializer() {
return elementJsonDeserializer;
}
#Override
public Element deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
throws JsonParseException {
final int typeCode = jsonElement.getAsJsonObject().getAsJsonPrimitive("type").getAsInt();
// Simple dispatching here
// RuntimeTypeAdapterFactory basically does the same
switch ( typeCode ) {
case 1:
return context.deserialize(jsonElement, Type1Element.class);
case 2:
return context.deserialize(jsonElement, Type2Element.class);
default:
throw new JsonParseException("Unrecognized type: " + typeCode);
}
}
}
Now get it all working together (response.json is your JSON document resource):
private static final Type type = new TypeToken<Response<List<Element>>>() {
}.getType();
private static final Gson gson = new GsonBuilder()
.registerTypeAdapter(Element.class, getElementJsonDeserializer())
.create();
public static void main(final String... args)
throws IOException {
try ( final JsonReader jsonReader = getPackageResourceJsonReader(Q43802350.class, "response.json") ) {
final Response<List<Element>> response = gson.fromJson(jsonReader, type);
response.data
.stream()
.map(Element::getClass)
.map(Class::getSimpleName)
.forEach(System.out::println);
}
}
Output:
Type1Element
Type2Element
Of course, don't forget to register the gson instance with GsonConverterFactory.create(gson) in your Retrofit builder.
I have a json format which I am converting into Java Object Model using Jackson API. I am using Jaxsonxml 2.1.5 parser. The json response is as shown below.
{
"response": {
"name": "states",
"total-records": "1",
"content": {
"data": {
"name": "OK",
"details": {
"id": "1234",
"name": "Oklahoma"
}
}
}
}
}
Now json response format has changed. If the total-records is 1 the details will be an object with id and name attributes. But if the total-records is more than 1 then the details will be an array of object like below:
{
"response": {
"name": "states",
"total-records": "4",
"content": {
"data": {
"name": "OK",
"details": [
{
"id": "1234",
"name": "Oklahoma"
},
{
"id": "1235",
"name": "Utah"
},
{
"id": "1236",
"name": "Texas"
},
{
"id": "1237",
"name": "Arizona"
}
]
}
}
}
}
My Java Mapper class looks like below with earlier json response.
#JsonIgnoreProperties(ignoreUnknown = true)
public class MapModelResponseList {
#JsonProperty("name")
private String name;
#JsonProperty("total-records")
private String records;
#JsonProperty(content")
private Model model;
public Model getModelResponse() {
return model;
}
public void setModel(Model model) {
this.model = model;
}
}
Client Code
package com.test.deserializer;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com..schema.model.Person;
public class TestClient {
public static void main(String[] args) {
String response1="{\"id\":1234,\"name\":\"Pradeep\"}";
TestClient client = new TestClient();
try {
Person response = client.readJSONResponse(response1, Person.class);
} catch (Exception e) {
e.printStackTrace();
}
}
public <T extends Object> T readJSONResponse(String response, Class<T> type) {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
T result = null;
try {
result = mapper.readValue(response, type);
} catch (Exception e) {
e.printStackTrace();
}
return (T) result;
}
}
Now based on the total-records how to handle to mapping to either a Model or list of Model Object. Please let me know.
You need a custom deserializer. The idea is to mix and match object processing with tree processing. Parse objects where possible but use the tree (JSONNode) for custom handling.
On the MapModelResponseList, remove the records property and add a List<Data> array where Data is just a holder class for the id/name pairs. You can get the total records by returning the size of this list.
In the deserializer, do the following:
public final class MapModelDeserializer extends BeanDeserializer {
public MapModelDeserializer(BeanDeserializerBase src) {
super(src);
}
protected void handleUnknownProperty(JsonParser jp, DeserializationContext ctxt, Object beanOrClass, String propName) throws IOException, JsonProcessingException {
if ("content".equals(propName)) {
MapModelResponseList response = (MapModelResponseList) beanOrClass;
// this probably needs null checks!
JsonNode details = (JsonNode) jp.getCodec().readTree(jp).get("data").get("details");
// read as array and create a Data object for each element
if (details.isArray()) {
List<Data> data = new java.util.ArrayList<Data>(details.size());
for (int i = 0; i < details.size(); i++) {
Data d = jp.getCodec().treeToValue(details.get(i), Data.class);
data.add(d);
}
response.setData(data);
}
// read a single object
else {
Data d = jp.getCodec().treeToValue(details, Data.class);
response.setData(java.util.Collections.singletonList(d));
}
super.handleUnknownProperty(jp, ctxt, beanOrClass, propName);
}
Note that you do not implement deserialize() - the default implementation is used to create the MapModelResponseList as normal. handleUknownProperty() is used to deal with the content element. Other data you don't care about is ignored due to #JsonIgnoreProperties(ignoreUnknown = true) in the super call.
This is a late answer, but I solve it in a different way. It can work by catching it in Object like this:
#JsonProperty("details")
public void setDetails(Object details) {
if (details instanceof List) {
setDetails((List) details);
} else if (details instanceof Map) {
setDetails((Map) details);
}
}
public void setDetails(List details) {
// your list handler here
}
public void setDetails(Map details) {
// your map handler here
}
My Json looks something like (and its unmodifiable)
{
....
"Sale": [
"SaleLines": {
"SaleLine": [
{
"Item": {
"Prices": {
"ItemPrice": [
{
"amount": "100",
"useType": "Default"
},
{
"amount": "100",
"useType": "MSRP"
}
]
},
}
......
......
}
]
"calcDiscount": "0",
"calcSubtotal": "500",
}
]
}
The java POJO code looks like
public static class SaleLines {
#JsonProperty("SaleLine")
private SaleLineObject[] saleLineObject;
public SaleLineObject[] getSaleLineObject() { return saleLineObject; }
public void setSaleLineObject(SaleLineObject[] saleLineObject) { this.saleLineObject = saleLineObject; }
}
public static class SaleLineObject {
private SaleLine saleLine;
public SaleLine getSaleLine() {
return saleLine;
}
public void setSaleLine(SaleLine saleLine) {
this.saleLine = saleLine;
}
}
public static class SaleLine {
#JsonProperty("itemID")
private String itemId; //line_item_nk
#JsonProperty("unitPrice")
private String unitPrice;
....
}
#JsonPropertyOrder({"total", "calcSubTotal", "calcDiscount"})
public static class Sale {
private String saleTotal, calcSubtotal, calcDiscount;
private int salesValueWOVat;
#JsonProperty("SaleLines")
SaleLines saleLine;
#JsonCreator
public Sale (#JsonProperty("total")String saleTotal,
#JsonProperty("calcSubtotal")String calcSubtotal,
#JsonProperty("calcDiscount")String calcDiscount,
#JsonProperty("SaleLines")SaleLines saleLine,
) {
this.saleTotal = saleTotal;
this.calcSubtotal = calcSubtotal;
this.calcDiscount = calcDiscount;
this.saleLine = saleLine;
setSalesValueWOVat();
}
// getter and setters
}
#SuppressWarnings({ "rawtypes" })
public static <E, T extends Collection> T readFromJsonAndFillType (
String json,
Modules module,
Class <T> collectionType,
Class <E> elementType)
throws JsonParseException, JsonMappingException, IOException {
ObjectMapper objMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
TypeFactory tf = objMapper.getTypeFactory();
JsonNode node = objMapper.readTree(json).get(module.jsonFieldName);
return objMapper.readValue(node.toString(),
tf.constructCollectionType(collectionType, elementType));
}
In main
ArrayList<Sale> saleList = readFromJsonAndFillType(
saleJSON,
Modules.SALE,
ArrayList.class,
Sale.class);
for (Sale sale: saleList) {
System.out.println(sale.toString());
}
I know this question has been asked multiple times and even I took help from for eg
Can not deserialize instance of java.util.ArrayList out of START_OBJECT token
But still I cannot get through this error
I know this question has been asked multiple times & everyone getting resolved there problems with different ways. Whenever you find "Can not deserialized instance of out of START_OBJECT token". it's generally occur when you trying to get object which is not actually same in json format (means json starting object is different not as you guys are converting).
For Ex:- Json returning first object is Boolean but unfortunately you are converting is to List<Object> then you will having this error.
I would suggest to have a look to read format using below code than convert it as per the object returning.
ObjectMapper objectMapper = new ObjectMapper();
Map<?,?> empMap = objectMapper.readValue(new FileInputStream("employee.json"),Map.class);
for (Map.Entry<?,?> entry : empMap.entrySet())
{
System.out.println("\n----------------------------\n"+entry.getKey() + "=" + entry.getValue()+"\n");
}
Get the key & convert the value as per the object returning.
For reference:- https://dzone.com/articles/processing-json-with-jackson
From the land of .NET I have a generic class define like so..
public class SyncWrapper<T, I>
{
public IList<T> Data { get; set; }
public IList<I> DeleteIds { get; set; }
public DateTime LastSyncDateTime { get; set; }
}
I was able to create an instance of this object from json by simply calling ...
JsonConvert.DeserializeObject<SyncWrapper<T, Guid>>(json);
Now I've been given the task of porting this code over to Java/Android. Having never touched Java before, I've a lot to learn!
Anyway, so far I've tried Gson and Jackson to get the object from json but no joy. I think that I won't be able to call andthing with the <T> involved gson.fromJson(json, SyncWrapper<T, UUID>.class) for example as there is a problem with type Erasure!
My efforts so far have looked like this....
Gson
Gson gson = new Gson();
SyncWrapper<MyClass, UUID> result = gson.fromJson(json, new TypeToken<SyncWrapper<MyClass, UUID>>() { }.getType());
This compiles but the result is an empty SyncWrapper
Jackson
ObjectMapper mapper = new ObjectMapper();
SyncWrapper<MyClass, UUID> result = mapper.readValue(json, new TypeReference<SyncWrapper<MyClass, UUID>>() { });
This compiles but crashes the app when executed!!!
My Java version of SyncWrapper....
public class SyncWrapper<T, I> {
private DateTime lastSyncDateTime;
private Collection<T> data;
private Collection<I> deleteIds;
public Collection<T> getData() {
return data;
}
public void setData(Collection<T> data) {
this.data = data;
}
public Collection<I> getDeleteIds() {
return deleteIds;
}
public void setDeleteIds(Collection<I> deleteIds) {
this.deleteIds = deleteIds;
}
public DateTime getLastSyncDateTime() {
return lastSyncDateTime;
}
public void setLastSyncDateTime(DateTime lastSyncDateTime) {
this.lastSyncDateTime = lastSyncDateTime;
}
}
I've been really thrown in at the deep end by the powers that be (all programming is the same isn't it?), so any help really appreciated.
I'm not precious about which library I use (Gson, Jackson, etc)
Update
An example of the Json that is to be deserialized...
{
"Data": [
{
"Name": "Company A",
"Id": "7d5d236c-c2b5-42dc-aea5-99e6752c8a52"
},
{
"Name": "Company B",
"Id": "44444444-0000-0000-0000-444444444444"
},
{
"Name": "Company C",
"Id": "249a4558-05c6-483f-9835-0056804791c9"
}
],
"DeleteIds": [
"5f7873a6-b2ee-4566-9714-1577b81384f4",
"1f224a39-16c3-441d-99de-8e58fa8f31c2"
],
"LastSyncDateTime": "\/Date(1393580073773+0000)\/"
}
..or this (more often than not, the DeleteIds will be null)...
{
"Data": [
{
"Name": "Company A",
"Id": "7d5d236c-c2b5-42dc-aea5-99e6752c8a52"
},
{
"Name": "Company B",
"Id": "44444444-0000-0000-0000-444444444444"
},
{
"Name": "Company C",
"Id": "249a4558-05c6-483f-9835-0056804791c9"
}
],
"DeleteIds": null,
"LastSyncDateTime": "\/Date(1393580073773+0000)\/"
}
For the above json I would be mapping to a SyncWrapper where T is Company...
public class Company extends ModelBase {
private String name;
public Company(UUID id, String name) {
super(id);
setName(name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Here's the issues:
Your field names in your Java classes don't match the field names in the JSON; capitalization matters. This is why you're getting back absolutely nothing after parsing.
I'm going to go with Gson examples simply because I know that off the top of my head. You can do the same things in Jackson, but I'd need to look them up:
public class SyncWrapper<T, I> {
#SearializedName("LastSyncDateTime")
private DateTime lastSyncDateTime;
#SearializedName("Data")
private Collection<T> data;
#SearializedName("DeleteIds")
private Collection<I> deleteIds;
This tells Gson which fields in Java map to the fields in JSON. You could also go with a field naming policy instead, since it looks like all your fields are upper camel case:
Gson g = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
.build();
Now your fields will match up. The next issue is going to be that UUID class. That class in Java is not a string; it's a class that generates UUIDs. Just use String for the type that holds it in your Java class.
The DateTime class ... same issue. And on top of that you've got a bit of a weird value in your JSON for the date. You'll either want to store that as a String as well, or you're going to have to write a custom deserializer to deal with it.
With those changes, I think you're good to go.
Edit to add from the comments: If you really need the Java UUID class rather than just the String representation, you can write a chunk of code that takes care of this for you:
class UUIDDeserializer implements JsonDeserializer<UUID>
{
#Override
public UUID deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException
{
return UUID.fromString(je.getAsString());
}
}
You can then register this with Gson:
Gson g = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
.registerTypeAdapter(UUID.class, new UUIDDeserializer())
.build();
This will populate the UUID typed fields in your class with UUID instances. This is the same thing you'd need to do with that funky date value.
I suggest using Jackson for this; it has a more clear API and does not require creating a new type as Gson (where you have to extend a class to be able to do that).
Example:
public static <T> T fromJsonToGenericPojo(
String json, Class<?> classType, Class<?>... genericTypes) {
JavaType javaType = TypeFactory.defaultInstance()
.constructParametricType(classType, genericTypes);
try {
return OBJECT_MAPPER.readValue(json, javaType);
} catch (IOException e) {
LOGGER.error(e.getMessage(), e);
throw new IllegalArgumentException(e);
}
}