I have a JSON response which contains a timestamp, but I'm having some issues parsing it to a date object. I'm having the same difficulty serializing it back into the same format as well. I'm using Gson to decode the JSON responses to objects and this is the only spot I'm having a hicup with.
"TimeStamp":"\/Date(1327650052632+0000)\/"
I did have a look at the custom serialize/deserialize adapters, but couldn't make heads or tails from it.
Regards,
EZFrag
I managed to understand the adapters (sort of). Here is the code I used:
private Gson createGson(){
JsonSerializer<Date> ser = new JsonSerializer<Date>() {
#Override
public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext
context) {
return src == null ? null : new JsonPrimitive("\\/Date("+src.getTime()+"+0000)\\/");
}
};
JsonDeserializer<Date> deser = new JsonDeserializer<Date>() {
#Override
public Date deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
String tmpDate = json.getAsString();
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher(tmpDate);
boolean found = false;
while (matcher.find() && !found) {
found = true;
tmpDate = matcher.group();
}
return json == null ? null : new Date(Long.parseLong(tmpDate));
}
};
return new GsonBuilder().serializeNulls()
.registerTypeAdapter(Date.class, ser)
.registerTypeAdapter(Date.class, deser).create();
}
JQuery has this method .
formatJSONDate(Date(result.dateFromJSON))
or you may try this regular expression.
function parseJsonDate(jsonDate) {
var offset = new Date().getTimezoneOffset() * 60000;
var parts = /\/Date\((-?\d+)([+-]\d{2})?(\d{2})?.*/.exec(jsonDate);
if (parts[2] == undefined)
parts[2] = 0;
if (parts[3] == undefined)
parts[3] = 0;
return new Date(+parts[1] + offset + parts[2]*3600000 + parts[3]*60000);
};
Related
I'm trying to map different formats onto 3 xsd types, xs:dateTime, xs:date, and xs:time. I'm doing a bit of codegen in my project, and do not have a bindings file, though I do have a package-info.
#javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters({
#javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(value = com.codegen.pojo.lib.adapters.XmlDateTimeFormatter.class, type = com.codegen.pojo.lib.types.IXmlDateTime.class)
})
#javax.xml.bind.annotation.XmlSchemaTypes({
#javax.xml.bind.annotation.XmlSchemaType(name = "dateTime", type = com.codegen.pojo.lib.types.XmlDateTime.class),
#javax.xml.bind.annotation.XmlSchemaType(name = "date", type = com.codegen.pojo.lib.types.XmlDate.class),
#javax.xml.bind.annotation.XmlSchemaType(name = "time", type = com.codegen.pojo.lib.types.XmlTime.class)
})
#com.codegen.pojo.lib.annotations.XMLDateTimeFormat(format = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXX")
#com.codegen.pojo.lib.annotations.XMLDateFormat(format = "yyyy-MM-dd'T'")
#com.codegen.pojo.lib.annotations.XMLTimeFormat(format = "'T'HH:mm:ss.SSSXXXX")
package my_pkg;
All 3 of the Xml(Date/Time) classes mentioned in the XmlSchemaType annotations implement the IXmlDateTime interface.
The adapter:
public class XmlDateTimeFormatter extends XmlAdapter<String, IXmlDateTime> {
private final DateTimeFormatter dateTimeFormat;
private final DateTimeFormatter dateFormat;
private final DateTimeFormatter timeFormat;
public XmlDateTimeFormatter(String dateTimeFormatPattern, String dateFormatPattern, String timeFormatPattern) {
dateTimeFormat = dateTimeFormatPattern == null ? null : DateTimeFormatter.ofPattern(dateTimeFormatPattern);
dateFormat = dateFormatPattern == null ? null : DateTimeFormatter.ofPattern(dateFormatPattern);
timeFormat = timeFormatPattern == null ? null : DateTimeFormatter.ofPattern(timeFormatPattern);
}
#Override
public String marshal(IXmlDateTime dateTime) throws Exception {
if (dateTime instanceof XmlDateTime) {
return dateTimeFormat.format(dateTime.getCalendar().toGregorianCalendar().toZonedDateTime());
} else if (dateTime instanceof XmlDate) {
return dateFormat.format(dateTime.getCalendar().toGregorianCalendar().toZonedDateTime());
} else if (dateTime instanceof XmlTime) {
return timeFormat.format(dateTime.getCalendar().toGregorianCalendar().toZonedDateTime());
}
return null;
}
#Override
public IXmlDateTime unmarshal(String dateTime) throws Exception {
XMLGregorianCalendar calendar = DatatypeFactory.newInstance().newXMLGregorianCalendar(dateTime);
QName xmlFormat = calendar.getXMLSchemaType();
if (xmlFormat.equals(DatatypeConstants.DATETIME)) {
return new XmlDateTime(calendar);
} else if (xmlFormat.equals(DatatypeConstants.DATE)) {
return new XmlDate(calendar);
} else if (xmlFormat.equals(DatatypeConstants.TIME)) {
return new XmlTime(calendar);
}
return null;
}
The adapter is added to a Marshaller/Unmarshaller created from a JAXBContext. Unfortunately, neither the marshal or unmarshal get called. Any thoughts on how I could fix this?
I would like to cut too long strings in json.
In order to do that I would like to register new type adapter for String type and inside this deserializer I will check and limit too long strings.
Gson gson = new GsonBuilder().registerTypeAdapter(String.class, new CuttingStringDeserializer()).create();
JsonElement element = gson.fromJson(jsonString, JsonElement.class);
return new GsonBuilder().setPrettyPrinting().create().toJson(element);
Example of json file that I want to process:
{
"myString": "this string is too long - cut it",
"other": "this is ok"
}
Desired output:
{
"myString": "this strin",
"other": "this is ok"
}
In general I don't know structure of json but I want to filter all string occurrences.
Deserializer:
public class CuttingStringDeserializer implements JsonDeserializer<String> {
#Override
public String deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
throws JsonParseException {
String s = json.getAsString();
if(s.lenght() > MAX_CONTENT_LENGTH){
return s.substring(0, MAX_CONTENT_LENGTH);
}else{
return s;
}
}
Unfortunately my custom deserializer is not called by gson.
This (using some custom JsonWriter) works:
package so41793888;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.stream.JsonWriter;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import java.io.IOException;
import java.io.StringWriter;
public class Demo {
public static void main(String[] args) {
String jsonString = "{\n" +
" \"myString\": \"this string is too long - cut it\",\n" +
" \"other\": \"this is ok\"\n" +
"}";
Gson gson = new GsonBuilder().create();
JsonElement element = gson.fromJson(jsonString, JsonElement.class);
StringWriter out = null;
try {
out = new StringWriter();
new GsonBuilder().create().toJson(element, new MyJsonWriter(out));
System.out.println(out.getBuffer().toString());
} finally {
IOUtils.closeQuietly(out);
}
}
private static class MyJsonWriter extends JsonWriter {
public MyJsonWriter(final StringWriter out) {
super(out);
setIndent(" ");
}
#Override
public JsonWriter value(final String value) throws IOException {
return super.value(StringUtils.abbreviate(value, 12));
}
}
}
outputs:
{
"myString": "this stri...",
"other": "this is ok"
}
You can reject the idea of tree processing (the way how JsonSerializer and JsonDeserializer work) in favor of stream processing, where you analyze every token on your own. GsonBuilder seems not to allow overriding a streaming-fashioned TypeAdapters as well, but you can then use JsonReader in order to parse every token from an input stream, and JsonWriter to emit processed tokens to an output stream. This may look too low level, but since it's a streaming way, it is really cheap and does not consume much memory as tree processing usually does. Thus you can process even infinite streams.
#SuppressWarnings("resource")
private static void trim(final int maxStringLength, final Reader reader, final Writer writer)
throws IOException {
// a specifically configured IDEA complains for the unclosed jsonReader, but invoking the `close` method is a like a chain and sometimes undesirable
#SuppressWarnings("all")
final JsonReader jsonReader = new JsonReader(reader);
// the same goes to jsonWriter
#SuppressWarnings("all")
final JsonWriter jsonWriter = new JsonWriter(writer);
for ( JsonToken token; (token = jsonReader.peek()) != END_DOCUMENT; ) {
switch ( token ) {
case BEGIN_ARRAY:
// merely reflect a BEGIN_ARRAY token
jsonReader.beginArray();
jsonWriter.beginArray();
break;
case END_ARRAY:
// merely reflect an END_ARRAY token
jsonReader.endArray();
jsonWriter.endArray();
break;
case BEGIN_OBJECT:
// merely reflect a BEGIN_OBJECT token
jsonReader.beginObject();
jsonWriter.beginObject();
break;
case END_OBJECT:
// merely reflect an END_OBJECT token
jsonReader.endObject();
jsonWriter.endObject();
break;
case NAME:
// merely reflect NAME tokens (or trim?)
jsonWriter.name(jsonReader.nextName());
break;
case STRING:
// trimming a STRING token if necessary
final String string = jsonReader.nextString();
jsonWriter.value(string.length() > maxStringLength ? string.substring(0, maxStringLength) : string);
break;
case NUMBER:
// NUMBER tokens are a bit complex because JSON only denotes a double number that can be literally an integer
final String rawNumber = jsonReader.nextString();
try {
// try to write the biggest integer number supported by Java, floating points also fail to be parsed as long values
jsonWriter.value(parseLong(rawNumber));
} catch ( final NumberFormatException nex1 ) {
try {
// not a long value, then perhaps it's a double value?
jsonWriter.value(parseDouble(rawNumber));
} catch ( final NumberFormatException nex2 ) {
// can't think of specific cases here...
throw new AssertionError("Must not happen", nex2);
}
}
break;
case BOOLEAN:
// merely reflect BOOLEAN tokens
jsonWriter.value(jsonReader.nextBoolean());
break;
case NULL:
// merely reflect NULL tokens
jsonReader.nextNull();
jsonWriter.nullValue();
break;
case END_DOCUMENT:
// fall through, because this type of tokens is checked above, and it's fine to throw an assertion error
default:
throw new AssertionError(token);
}
}
}
This method, of course, does not support pretty printing, but it can be easily implemented if it's really necessary.
And how it's used:
final Reader reader = new StringReader("{\"myString\":\"this string is too long - cut it\",\"other\":\"this is ok\"}");
final Writer writer = new OutputStreamWriter(System.out); // redirect the output to System.out
trim(10, reader, writer);
writer.flush(); // flushing at a call-site, we decide
The output:
{"myString":"this strin","other":"this is ok"}
The solution can work with any kind of JSON, having no background for a particular type. Simply speaking, it's just type-unaware and can process even simple single literals like "foo-bar-baz-qux".
It seems for me it doesn't make sense what are you trying to archive, but here kick off code which should help .
public class Main {
private static String json = "{\"myString\": \"this string is too long - limit it\",\"other\": \"this is ok\"}";
public static void main(String... var) {
System.out.print(cutJson(json));
}
public static String cutJson(String json) {
Type type = new TypeToken<Map<String, String>>() {
}.getType();
Gson gson = new GsonBuilder().registerTypeAdapter(type, new CuttingStringDeserializer()).create();
Map<String, String> element = gson.fromJson(json, type);
return new GsonBuilder().setPrettyPrinting().create().toJson(element);
}
private static class CuttingStringDeserializer implements JsonDeserializer<Map<String, String>> {
#Override
public Map<String, String> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Iterator<Map.Entry<String, JsonElement>> iterator = ((JsonObject) json).entrySet().iterator();
Map<String, String> result = new HashMap<String, String>();
while (iterator.hasNext()) {
Map.Entry<String, JsonElement> entry = iterator.next();
if (entry.getValue().getAsString().length() > 10) {
entry.setValue(new JsonPrimitive(entry.getValue().getAsString().substring(0, 9)));
}
result.put(entry.getKey(), entry.getValue().getAsString());
}
return result;
}
}
}
Prints:
{
"myString": "this stri",
"other": "this is ok"
}
I have a class MySimpleObject that have various member fields. Given a json, it will populate the field accordingly. However if the json is stated as "nil", I plan to set it to null instead of string "nil".
The below example should result is an MySimpleObject with null for all it's fields, and a 0 length list of subItemList. myObj1 should be equal to myObj2.
#Test
public void myTestFunction() {
String myJson1 = "{\"item1\":\"nil\",\"item2\":\"nil\",\"subItemList\":[{\"subItem1\":\"nil\",\"subItem2\":\"nil\"}]}";
String myJson2 = "{\"subItemList\":[]}";
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(new TypeToken<List<MySubItems>>(){ }.getType(), new MyOwnListDeserializer());
gsonBuilder.registerTypeAdapter(String.class, new MyOwnStringDeserializer());
Gson gson = gsonBuilder.create();
MySimpleObject myObj1 = gson.fromJson(myJson1, MySimpleObject.class);
MySimpleObject myObj2 = gson.fromJson(myJson2, MySimpleObject.class);
assertThat(myObj1.equals((myObj2))).isTrue();
}
class MySimpleObject implements Serializable {
String item1 = null;
String item2 = null;
List<MySubItems> subItemList;
#Override
public int hashCode() {
int hash = 17;
hash = 31*hash + ((item1 == null)? 0 :item1.hashCode());
hash = 31*hash + ((item2 == null)? 0 :item2.hashCode());
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj instanceof MySimpleObject) {
return this.hashCode() == obj.hashCode();
}
return super.equals(obj);
}
}
class MySubItems implements Serializable {
String subItem1 = null;
String subItem2 = null;
#Override
public int hashCode() {
int hash = 17;
hash = 31*hash + ((subItem1 == null)? 0 :subItem1.hashCode());
hash = 31*hash + ((subItem2 == null)? 0 :subItem2.hashCode());
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj instanceof MySubItems) {
return this.hashCode() == obj.hashCode();
}
return super.equals(obj);
}
}
How to write the custom serializer without having to loop through each jsonObject and check for "nil" to set to null?
I looked at the Gson library and also the gson-fire project, but none of them seem to allow for a real generic (and performant) solution.
One way to go is to systematically replace "nil" by "null" in the json string before passing it to the gson object. It is not very clean, but it is quite performant and could work.
Here is a basic method (must be refined):
public static String convertNil( String json ){
return json.replaceAll( ":\\s*\"nil\"", ": null" );
}
Then use it like:
MySimpleObject myObj1 = gson.fromJson( convertNil( myJson1 ), MySimpleObject.class );
MySimpleObject myObj2 = gson.fromJson( convertNil( myJson2 ), MySimpleObject.class );
I manage to come up with some custom deserialization that do the job as below.
#Test
public void myTestFunction() {
String myJson1 = "{\"item1\":\"nil\",\"item2\":\"nil\",\"subItemList\":[{\"subItem1\":\"nil\",\"subItem2\":\"nil\"}]}";
String myJson2 = "{\"subItemList\":[]}";
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(new TypeToken<List<MySubItems>>(){ }.getType(), new MyOwnListDeserializer());
gsonBuilder.registerTypeAdapter(String.class, new MyOwnStringDeserializer());
Gson gson = gsonBuilder.create();
MySimpleObject myObj1 = gson.fromJson(myJson1, MySimpleObject.class);
MySimpleObject myObj2 = gson.fromJson(myJson2, MySimpleObject.class);
assertThat(myObj1.equals((myObj2))).isTrue();
}
class MySimpleObject implements Serializable {
String item1 = null;
String item2 = null;
List<MySubItems> subItemList;
#Override
public int hashCode() {
int hash = 17;
hash = 31*hash + ((item1 == null)? 0 :item1.hashCode());
hash = 31*hash + ((item2 == null)? 0 :item2.hashCode());
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj instanceof MySimpleObject) {
return this.hashCode() == obj.hashCode();
}
return super.equals(obj);
}
}
class MySubItems implements Serializable {
String subItem1 = null;
String subItem2 = null;
#Override
public int hashCode() {
int hash = 17;
hash = 31*hash + ((subItem1 == null)? 0 :subItem1.hashCode());
hash = 31*hash + ((subItem2 == null)? 0 :subItem2.hashCode());
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj instanceof MySubItems) {
return this.hashCode() == obj.hashCode();
}
return super.equals(obj);
}
}
class MyOwnStringDeserializer implements JsonDeserializer<String> {
#Override
public String deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return (json.getAsString().equals("nil"))? null : json.getAsString();
}
}
class MyOwnListDeserializer implements JsonDeserializer<List<MySubItems>> {
#Override
public List<MySubItems> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
List<MySubItems> list = new ArrayList<>();
for (JsonElement element : json.getAsJsonArray()) {
JsonObject subObj = element.getAsJsonObject();
MySubItems subItems = new MySubItems();
if (!subObj.get("subItem1").getAsString().equals("nil")) {
subItems.subItem1 = subObj.get("subItem1").getAsString();
}
if (!subObj.get("subItem2").getAsString().equals("nil")) {
subItems.subItem2 = subObj.get("subItem1").getAsString();
}
if (subItems.subItem1 != null || subItems.subItem2 != null) {
list.add(subItems);
}
}
return (list.size() == 0)? null : list;
}
}
I'm still not very happy that MyOwnListDeserializer, has to manually handle subItem1 and subItem2, despite they should have the same rule as defined by MyOwnStringDeserializer. But I just don't know how to apply MyOwnStringDeserializer to MyOwnListDeserializer.
Would be still open for other better answers than mine.
Update A more optimized answer could be found in
https://stackoverflow.com/a/39671580/3286489
I have a JSON string that I want to deserilizie using Gson - {"Id":3,"Title":"Roskilde","Description":"Famous Danske festival","StartingTime":"2016-06-12T00:00:00","Duration":"02:02:00"}
The error I get when I try to deserilize the Duration field:
Unparseable date: "02:02:00"
The deserilizer(my idea was to add two possible formats of date deserialization):
Gson gSon= new GsonBuilder().registerTypeAdapter(Date.class, new DateDeserializer()).create();
private static final String[] DATE_FORMATS = new String[] {
"yyyy-MM-dd'T'HH:mm:ss",
"HH:mm:ss"
};
private class DateDeserializer implements JsonDeserializer<Date> {
#Override
public Date deserialize(JsonElement jsonElement, Type typeOF,
JsonDeserializationContext context) throws JsonParseException {
for (String format : DATE_FORMATS) {
try {
return new SimpleDateFormat(format, Locale.US).parse(jsonElement.getAsString());
} catch (ParseException e) {
}
}
throw new JsonParseException("Unparseable date: \"" + jsonElement.getAsString()
+ "\". Supported formats: " + Arrays.toString(DATE_FORMATS));
}
}
and my Event Class(as you can see "Duration" is not of a type date - it's of the type Time - what should I do to make the deserilizer read Duration as type time not date?
private int Id;
private String Title;
private String Description;
private Date StartingTime;
private Time Duration;
public Event(int id, String title,String description, String place, Date startingTime, Time duration)
{
this.Id = id;
this.Description = description;
this.Title = title;
this.StartingTime = startingTime;
this.Duration = duration;
}
Add another class for Time deserialization - make sure you convert the return value to "Time".
(Also see: cast a String to sql time)
private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
private static final String TIME_FORMAT = "HH:mm:ss";
private class DateDeserializer implements JsonDeserializer<Date> {
#Override
public Date deserialize(JsonElement jsonElement, Type typeOF,
JsonDeserializationContext context) throws JsonParseException {
try {
return new SimpleDateFormat(DATE_FORMAT, Locale.US).parse(jsonElement.getAsString());
} catch (ParseException e) {
}
throw new JsonParseException("Unparseable date: \"" + jsonElement.getAsString()
+ "\". Supported formats: " + DATE_FORMAT);
}
}
private class TimeDeserializer implements JsonDeserializer<Time> {
#Override
public Time deserialize(JsonElement jsonElement, Type typeOF,
JsonDeserializationContext context) throws JsonParseException {
try {
String s = jsonElement.getAsString();
SimpleDateFormat sdf = new SimpleDateFormat(TIME_FORMAT, Locale.US);
sdf.parse(s);
long ms = sdf.parse(s).getTime();
Time t = new Time(ms);
return t;
} catch (ParseException e) {
}
throw new JsonParseException("Unparseable time: \"" + jsonElement.getAsString()
+ "\". Supported formats: " + TIME_FORMAT);
}
}
Register both classes:
GsonBuilder gSonBuilder= new GsonBuilder();
gSonBuilder.registerTypeAdapter(Date.class, new DateDeserializer());
gSonBuilder.registerTypeAdapter(Time.class, new TimeDeserializer());
Gson gSon = gSonBuilder.create();
You should register a deserializer for both Date and Time. Something like this:
try{
// Convert JSON-string to a List of Event objects
Type listType = new TypeToken<ArrayList<Event>>(){}.getType();
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Date.class, new JsonDeserializer<Date>() {
#Override
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
try{
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH);
return df.parse(json.getAsString());
}
catch(ParseException ex){
return null;
}
});
gsonBuilder.RegisterTypeAdapter(Time.class, new JsonDeserializer<Time>(){
#Override
public Time deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
try{
DateFormat df = new SimpleDateFormat("HH:mm:ss", Locale.ENGLISH);
return df.parse(json.getAsString());
}
catch(ParseException ex){}
}
return null;
}
});
Gson eventGson = gsonBuilder.create();
events = dateGson.fromJson(json, listType);
}
catch(JsonParseException ex){
ex.printStackTrace();
}
PS: This code is untested, since I didn't had a lot of time. But I think you get the general idea. You just need two JsonDeserializers, one for time and one for Date.
JAva
I have the following problem.
I have a mobile object.
Calss object mobile
private ObjectId _id;
private String number;
private String nroMatricula;
Get and Set, etc.
When recovery of MongoDB.
String id = "5089e5fde4b07bf6f368366b";
DBObject soid = new BasicDBObject("_id", new ObjectId(id));
String s = MongoAccess.getSingleton().GetOneValueByKey("mobile", soid);
Mobile m = js.fromJson(s, Mobile.class);
public String GetOneValueByKey(String thecollection, DBObject key)
{
String result = null;
try {
DBCollection collection = MDB.getCollection(thecollection);
result = JSON.serialize(collection.findOne(key));
} catch (Exception e) {
Logger.getLogger(MongoAccess.class.getName()).log(Level.SEVERE, e.toString());
}
return result;
}
Recovery data correctly but the _id attribute.
I load the data I have in the database, but it generates a new id.
That should be the correct _id "5089e5fde4b07bf6f368366b" but that is not the charge on the object.
I can help.
Solution
JsonDeserializer<ObjectId> des = new JsonDeserializer<ObjectId>() {
#Override
public ObjectId deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException {
return new ObjectId(je.getAsJsonObject().get("$oid").getAsString());
}
};
Gson gson = new GsonBuilder().registerTypeAdapter(ObjectId.class, des).create();