I was trying to parse a JSON object using GSON parser and write into database using JDBC. However some of the fields are quite large to store in string datatype after I parse them using GSON parser, and also creates a problem as I would have to increase my column size in database for storing larger strings.
My class structure looks similar to this:
public class Results {
public String _refObjectUUID;
public String _refObjectName;
public String _type;
public String _CreatedAt;
public String Description;
public String Notes;
public String Theme;
public String StartDate;
public String EndDate;
public String State;
public String PlannedVelocity;
public String FormattedID;
public Owner Owner;
public Project Project;
public String ScheduleState;
public String Ready;
public Release Release;
}
While parsing by using the above class, some of the fields like Description for example are quite large and variable in length. So I wanted to how GSON would support such large string? Is there something similar to CLOB datatype that GSON supports?
Related
In my application, I am trying to get an OAuth token from multiple different providers.
According to the OAuth Spec, scopes should be sent as a string that contains a space delimited list of scopes.
However, I have noticed that some implementations return the scope as a List of strings.
In my application, I would like to store the scopes as a string that contains a space delimited list of scopes, as per the specification.
To do so, I created an Dto that looks as such (this dto is the same as the entity):
public class OAuthTokenDto {
// variables named to reflect OAuth Spec sends them to us
private String access_token;
private String refresh_token;
private Integer expires_in;
private String scope;
private String token_type;
public OAuthTokenDto() {
}
...
}
Is there a way to handle the fact that scopes might be sent as a List? In its current state, I am using rest template to do the mapping and it throws an exception because of this mismatch
You can make use of Jackson's #JsonAnyGetter and #JsonAnySetter. What you can do is, you can catch the unspecified tokens with this in which case your scope element and then typecast depending on the object type.
public class OAuthTokenDto {
// variables named to reflect OAuth Spec sends them to us
private String access_token;
private String refresh_token;
private Integer expires_in;
private String token_type;
#JsonIgnore
private Map<String, Object> properties = new HaspMap<String, Object>();
public OAuthTokenDto() {
}
#JsonAnyGetter
public Map<String, Object> getProperties() {
return this.properties;
}
#JsonAnySetter
public void setProperty(String name, Object value) {
this.properties.put(name, value);
}
}
And you can verify if your scope is an object or list of objects as follows
if(properites.get("scope") instanceof List<String>)
{
List<String> scopeList = properties.get("scope");
}
else
{
String scope = properties.get("scope");
}
Thanks to #Deadpool, the following implementation works
From jackson 2.6 you can use JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY
#JsonProperty("scope")
#JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
private String[] scope;
Clean solution:
Create converter class:
class ScopeConverter extends StdConverter<List<String>, String> {
#Override
public String convert(List<String> scopes) {
return scopes.stream().collect(Collectors.joining(" "));
}
}
Use it for scope property:
#JsonDeserialize(converter = ScopeConverter.class)
public String scope;
Enable:
mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
Now you can deserialize both cases scope being List and one element (with space separated scopes)
I have the following MongoDB Repository
public interface TeamRepository extends MongoRepository<Team, TeamId> {
....
}
And the following classes:
public abstract class DbId implements Serializable {
#Id
private final String id;
public DbId(final String id) { this.id = id;}
public String getId() { return id;}
}
public class TeamId extends DbId {
public TeamId(final String id) {
super(id)
}
}
As you can see, I have like a custom id for the repository (I have MongoRepository instead of something like MongoRepository). But, when I am trying to save a Team object, I get an error saying that MongoDB does not know how to generate DBId. Any clue?
MongoDb (or any database) would not know how to generate a string ID without you informing it what the value of the string is.
The default #Id is a string representation of ObjectId, which can be auto-generated by MongoDB. If you are changing the type of string ObjectId to a class, then at least the class needs to define:
** Conversion to string (serialisable), for example:
#Override
public String toString() {
return String.format(
"TeamID[uniqueString=%s]",
myUniqueString);
}
** How to generate the Id.
You can define a method in your TeamRepository i.e. save() to specify how your string can be generated. Alternatively you can check out
https://www.mkyong.com/mongodb/spring-data-mongodb-auto-sequence-id-example/
Where the example specify getNextSequenceId() to generate NumberLong custom id. Hopefully that guides you to your answer.
I am trying to use spring data mongodb to retrieve a list of collections from the DB. However as soon as my code runs I get the following exception:
org.springframework.data.mapping.model.MappingException: No mapping metadata found for java.lang.String
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:209)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readValue(MappingMongoConverter.java:1008)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.access$100(MappingMongoConverter.java:75)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:957)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:924)
at org.springframework.data.mapping.model.PersistentEntityParameterValueProvider.getParameterValue(PersistentEntityParameterValueProvider.java:78)
at org.springframework.data.mapping.model.SpELExpressionParameterValueProvider.getParameterValue(SpELExpressionParameterValueProvider.java:63)
at org.springframework.data.convert.ReflectionEntityInstantiator.createInstance(ReflectionEntityInstantiator.java:70)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:232)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:212)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:176)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:172)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:75)
I can't seem to find the solution for this anywhere. Any help would be highly appreciated
Here is my java code which is used to retrieve the data:
public List<GateAppointment> listGateAppointments() {
List<GateAppointment> gateAppointments = null;
try{
MongoOperations mongoOperation = (MongoOperations)getMongoTemplate();
gateAppointments = mongoOperation.findAll(GateAppointment.class,COLLECTION_NAME);
}
catch(Exception e){
e.printStackTrace();
}
return gateAppointments;
}
And public static final String COLLECTION_NAME = "gateVisitAppointments";
The following is a sample of my DB data(Sorry, cant post image directly as I dont have 10 rep):
Please click to view DB Image
Following is my GateAppointment class file properties (The rest of the file has all the setters and getters):
package com.ig.avs.common.entity.db;
public class GateAppointment {
/**
* The Class GateAppointment.
*
*/
private String gate;
private String gateAppointmentNbr;
private String bol;
private String containerNbr;
private String iso;
private String line;
private String transactionType;
private String truckingCompany;
private String truckId;
private String appoinmentDate;
private String apSlot;
private String slotStartTime;
private String slotEndTime;
private String isMapped;
private String status;
I have this enum :
public enum DocumentTypes {
PDF("PDF Document"), JPG("Image files (JPG)"), DOC("Microsoft Word documents");
private final String displayName;
DocumentTypes(final String display) {
this.displayName = display;
}
#Override
public String toString() {
return this.displayName;
}
}
And a model like this :
#Entity
#Table(name = "documents")
public class Document extends Model {
#Id
public Long id;
#Constraints.Required
#Formats.NonEmpty
#Enumerated(EnumType.STRING)
#Column(length=20, nullable=false)
public DocumentTypes type;
#Constraints.Required
#Formats.NonEmpty
#Column(nullable=false)
public String document;
}
I match the enum using this in my controller :
DynamicForm form = form().bindFromRequest();
// ...
Document doc = new Document();
doc.type = DocumentTypes.valueOf(form.field("type").value());
doc.save();
The problem is that in database, it's stored as "Microsoft Word documents", but I would prefer to store it as DOC.
How can I do that?
You can define it very fine granular with the Anotation EnumMapping or EnumValue. This works with the old version org.avaje.ebean.
It seems that there was a complete rewrite of the code. In the actual version there is a different approach.
With gson, is it possible to use a custom deserializer / serializer only on certain fields? The user guide shows how to register an adapter for an entire type, not for specific fields. The reason why I want this is because I parse a custom date format and store it in a long member field (as a Unix timestamp), so I don't want to register a type adapter for all Long fields.
Is there a way to do this?
I also store Date values as long in my objects for easy defensive copies. I also desired a way to override only the date fields when serializing my object and not having to write out all the fields in the process. This is the solution I came up with. Not sure it is the optimal way to handle this, but it seems to perform just fine.
The DateUtil class is a custom class used here to get a Date parsed as a String.
public final class Person {
private final String firstName;
private final String lastName;
private final long birthDate;
private Person(String firstName, String lastName, Date birthDate) {
this.firstName = firstName;
this.lastName = lastName;
this.birthDate = birthDate.getTime();
}
public static Person getInstance(String firstName, String lastName, Date birthDate) {
return new Person(firstName, lastName, birthDate);
}
public String toJson() {
return new GsonBuilder().registerTypeAdapter(Person.class, new PersonSerializer()).create().toJson(this);
}
public static class PersonSerializer implements JsonSerializer<Person> {
#Override
public JsonElement serialize(Person person, Type type, JsonSerializationContext context) {
JsonElement personJson = new Gson().toJsonTree(person);
personJson.getAsJsonObject().add("birthDate", new JsonPrimitive(DateUtil.getFormattedDate(new Date(policy.birthDate), DateFormat.USA_DATE)));
return personJson;
}
}
}
When the class is serialized, the birthDate field is returned as a formatted String instead of the long value.
Don't store it as a long, use a custom type with a proper adapter. Inside your type, represent your data any way you want -- a long, why not.