Search code examples
jsonjaxbjerseyjax-rsgenson

Jersey/Genson: Unmarschalling single object array


Similar to Jersey: Json array with 1 element is serialized as object BUT on the client side. E.g. I recieve a JSON object where a field is an array regulary, but in case there is only one element, it is a single object.

{"fileInfo":[{"fileName":"weather.arff","id":"10"},"fileName":"supermarket.arff","id":"11"}]}

versus

{"fileInfo":{"fileName":"weather.arff","id":"10"}}

I'm parsing/unmarshalling the JSON using Jersey/Genson. Of course, if the JSON doesnt match the target class I recieve an error (such as expected [ but read '{' )

I've read a lot about this bug and how to avoid when creating JSON objects on the SERVER side, but I found nothing about how to handle this issus when dealing on the CLIENT side.

As always, I prefere the most codeless possibility if there are several solutions...

BTW: Moxy works but it does not marshal native Object-type objects which is another requirement...


Solution

  • Update

    Starting with Genson 1.3 release you can achieve it by enabling permissiveParsing:

    Genson genson = new GensonBuilder().usePermissiveParsing(true).create();
    

    Answer

    Uh, do you know what library produces this on server side? I am curious to see who is responsible for all those badly structured jsons out there...

    It is not yet supported in Genson. Originally because IMO people should not produce such dynamic json. Anyway, I opened an issue - this can be easily done, you can expect it to be present in the release coming next week.

    Otherwise here is a way to achieve it without breaking the existing mechanisms. You need to register a Factory that will use Gensons collections factory to create an instance of its standard collection converter. Then you will wrap this converter in another one that will handle the object to array logic. Here is the code (not codeless..., but if you wait a bit you won't have to code :)).

    import com.owlike.genson.convert.DefaultConverters.CollectionConverterFactory;
    import com.owlike.genson.convert.DefaultConverters.CollectionConverterFactory;
    
    
    class SingleObjectAsCollectionFactory implements Factory<Converter<Collection>> {
      // get the default factory
      Factory<Converter<Collection<?>>> defaultFactory = CollectionConverterFactory.instance;
    
    
      @Override
      public Converter<Collection> create(Type type, Genson genson) {
        // obtain an instance of the correct default converter for this type
        final CollectionConverter defaultConverter = (CollectionConverter) defaultFactory.create(type, genson);
    
        // wrap it in your own converter
        return new Converter<Collection>() {
    
          @Override
          public void serialize(Collection object, ObjectWriter writer, Context ctx) throws Exception {
            defaultConverter.serialize(object, writer, ctx);
          }
    
          @Override
          public Collection deserialize(ObjectReader reader, Context ctx) throws Exception {
            if (reader.getValueType() == ValueType.OBJECT) {
              Object object = defaultConverter.getElementConverter().deserialize(reader, ctx);
              Collection result = defaultConverter.create();
              result.add(object);
              return result;
            } else return defaultConverter.deserialize( reader, ctx );
          }
        };
      }
    }
    

    And then register it

    Genson genson = new GensonBuilder()
                      .withConverterFactory(new SingleObjectAsCollectionFactory())
                      .create();