shau-lok / google-gson

Automatically exported from code.google.com/p/google-gson
0 stars 0 forks source link

Allow fields to be json literals #326

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
In the jclouds project, we often encounter semi-structured data.  For example, 
the structure is known to a certain depth, and after which we'd like to defer 
parsing to an extension.

For example, a json schema could be known to have attributes, which are not 
typed.  We'd like to defer parsing of these attributes.

For example, I'd like to parse the following:

{
    "name": "elmo",
    "attributes": {
        "custom/1": {
            "favorite": "berries"
        },
        "custom/2": 1000
    }
}

into a class like this:

class Thing {
    String name;
    Map<String, String> attributes; 
}

Original issue reported on code.google.com by adrian.f...@gmail.com on 15 May 2011 at 12:28

GoogleCodeExporter commented 9 years ago
in jclouds we have a workaround to this which includes a wrapper type called 
JsonBall, so in the above example, it is Map<String, JsonBall> attributes.  

public class JsonBall implements java.io.Serializable, Comparable<String>, 
CharSequence {
   private final String value;

   @Override
   public String toString() {
      return value;
   }

   public JsonBall(double value) {
      this.value = value + "";
   }

   public JsonBall(int value) {
      this.value = value + "";
   }

   public JsonBall(long value) {
      this.value = value + "";
   }

   public JsonBall(String value) {
      this.value = quoteStringIfNotNumber(checkNotNull(value, "value"));
   }

   static String quoteStringIfNotNumber(String in) {
      if (Patterns.JSON_STRING_PATTERN.matcher(in).find() && !Patterns.JSON_NUMBER_PATTERN.matcher(in).find()) {
         return "\"" + in + "\"";
      }
      return in;
   }
--snip---

We've then used registerTypeAdapter into gson to short-circuit on the JsonBall 
type. 

However, to get this type to properly serialize/deserialze, we had to create an 
internal type called JsonLiteral, adding JsonWriter.value(JsonLiteral), which 
is called by Streams.write if the element is an instanceof JsonLiteral.  The 
user-level class JsonBall and the internal class JsonLiteral are hooked 
together with type adapter below:

   public static class JsonBallAdapterImpl implements JsonSerializer<JsonBall>, JsonDeserializer<JsonBall> {

      public JsonElement serialize(JsonBall src, Type typeOfSrc, JsonSerializationContext context) {
         return new JsonLiteral(src);
      }

      public JsonBall deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
            throws JsonParseException {
         return new JsonBall(json.toString());
      }

   }

Original comment by adrian.f...@gmail.com on 15 May 2011 at 12:36

GoogleCodeExporter commented 9 years ago
I believe that a better way would be to optionally mark members with an 
annotation ex. @Literal or something.  This would eliminate the forced type.  
Also open for any suggestions, as I'd like to eliminate the gson patches 
currently lying around in jclouds

Original comment by adrian.f...@gmail.com on 15 May 2011 at 12:38

GoogleCodeExporter commented 9 years ago
How about using JsonParser class in Gson and navigating the DOM and using Gson 
to deserialize selective elements?

Original comment by inder123 on 20 May 2011 at 9:39

GoogleCodeExporter commented 9 years ago
I feel that we do not want to support a feature like this; however, here is 
some examples and documentation on how this can be achieved on your own:

http://sites.google.com/site/gson/streaming
http://code.google.com/p/google-gson/source/browse/trunk/extras/src/main/java/co
m/google/gson/extras/examples/rawcollections/RawCollectionsExample.java

Original comment by joel.leitch@gmail.com on 3 Jun 2011 at 6:29

GoogleCodeExporter commented 9 years ago
I understand the reluctance, just I'd love to be able to properly osgify 
jclouds without the classclash issues we currently have due to this.  

I've tried again using gson 2.1, and things are certainly easier.  However, I 
can't seem to add a new JsonElement (JsonLiteral in this case) without patching 
two classes.  Maybe you have some ideas.

JsonWriter <- I'd like to be able to overload write(JsonLiteral), or otherwise 
write a string directly, but cannot as this class is new'ed directly in a few 
places.

https://github.com/jclouds/jclouds/blob/master/core/src/main/java/com/google/gso
n/stream/JsonWriter.java

Streams.write <- static method which is called by a few classes, and calls 
TypeAdapters.JSON_ELEMENT.write.  Same issue as above; I'd like to override 
this behavior.  In the mean time, I have an instance of check delegating to my 
patched version of JsonWriter above, delegating to  overloads .value()

https://github.com/jclouds/jclouds/blob/master/core/src/main/java/com/google/gso
n/internal/Streams.java

Original comment by adrian.f...@gmail.com on 2 Jan 2012 at 3:31

GoogleCodeExporter commented 9 years ago
Do you really need to add a new JsonElement type? I'd like to talk to you 
offline to understand what you can't do with the existing APIs...

Original comment by limpbizkit on 2 Jan 2012 at 5:31

GoogleCodeExporter commented 9 years ago
okie.  just sent offline mail. thx

Original comment by fernc...@gmail.com on 2 Jan 2012 at 5:51

GoogleCodeExporter commented 9 years ago
here's a valid workaroud:

https://github.com/jclouds/jclouds/blob/master/core/src/main/java/org/jclouds/js
on/internal/NullHackJsonLiteralAdapter.java
https://github.com/jclouds/jclouds/blob/master/core/src/test/java/org/jclouds/js
on/internal/NullHackJsonLiteralAdapterTest.java

Thanks tons for the help!

Original comment by adrian.f...@gmail.com on 2 Jan 2012 at 10:51