1

There is a function which returns a JSON formatted list of elements. Problem occurs when reading content from a result which contains element of descendant type.

As an example, consider the following definitions:

public class Fruits {
    public String description;
    public List<Fruit> fruits = new ArrayList<Fruit>();
}

public class Fruit {
    public String name;
    public int pieces;
}

public class Banana extends Fruit {
    public String origin;
}


@ApplicationScoped
@Provider
@Produces("application/json")
@Path("/fruit")
public class MyRestService {
    @GET
    @Path("/fruits")
    public Fruits getFruits() {
        Fruits myFruits = new Fruits();
        myFruits.description = "My fruits";

        Fruit myApple = new Fruit();
        myApple.name = "apple";
        myApple.pieces = 8;
        myFruits.fruits.add(myApple);

        Banana myBanana = new Banana();
        myBanana.name = "banana";
        myBanana.pieces = 5;
        myBanana.origin = "Ecuador";
        myFruits.fruits.add(myBanana);

        return myFruits;
    }
}

The resulting JSON is the following:

{  
   "description":"My fruits",
   "fruits":[  
      {  
         "name":"apple",
         "pieces":8
      },
      {  
         "name":"banana",
         "pieces":5,
         "origin":"Ecuador"
      }
   ]
}

Suppose we have this result in the content variable. I want to parse it as follows:

ObjectMapper objectMapper = new ObjectMapper();
Fruits fruits = objectMapper.readValue(content, Fruits.class);

The following exception is thrown:

Exception in thread "main" org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "origin" (Class myresttest.Fruit), not marked as ignorable
 at [Source: java.io.StringReader@38653ad5; line: 1, column: 104] (through reference chain: myresttest.Fruits["fruits"]->myresttest.Fruit["origin"])

Is it somehow possible to overcome this problem, without touching the server code?

Ilya Ovesnov
  • 4,079
  • 1
  • 27
  • 31
Csaba Faragó
  • 443
  • 1
  • 4
  • 14
  • based on the answer here , you can use this: http://stackoverflow.com/questions/4486787/jackson-with-json-unrecognized-field-not-marked-as-ignorable`objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);` to ignore the error but this may cause losing the value of `origin` – Yazan Apr 20 '16 at 06:19
  • That's a helpful but dangerous idea. I'll think it over! Thank you! – Csaba Faragó Apr 21 '16 at 09:37

2 Answers2

2

I suggest you use json-simple-1.1.1

You can get the JSonObject like this :

JSONObjec requestobj = (JSONObject) JSonValue.parse(request) ;

Note: request value should be a String representation of the JSON .

And than parse the requestobj fields to a new Fruits instance ... For example :

Fruits myFruits = new Fruits();

myFruits.description = (String) requestobj.getString("description");
HasS
  • 201
  • 1
  • 17
  • Do you mean parsing field by field? In the reality the input formatted JSON is thousands lines long, the problem is located in the collection nesting level 4, and on each level there might be dozens of attributes. Furthermore, this is only a small fraction of the total system. Therefore I want to make it automate as much as possible. – Csaba Faragó Apr 20 '16 at 08:35
  • Yes , I though you were dealing with few lines JSON... But looks like you are dealing with a bigger problem . I have not been dealing with such JSON's so far , but it was just a suggestion :) Will consider studying the jackson library @CsabaFaragó – HasS Apr 20 '16 at 09:04
1

First of all I believe you are using very old version of jackson. org.codehaus.jackson was a moved to the com.fasterxml.jackson I believe at least three years ago. You still can use it but i would recommend to use supported version (if it is possible).

The problem that when you define List Jackson doesn't know anything about any of the sub-classes of Fruit. To inform Jackson that this class has sub-classes and you don't want to loose the data from them you need to add @JsonTypeInfo and @JsonSubTypes.

class Fruits {
    public String description;
    public List<Fruit> fruits = new ArrayList<Fruit>();
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type", defaultImpl = Fruit.class)
@JsonSubTypes({@JsonSubTypes.Type(value = Fruit.class, name = "fruit"),
        @JsonSubTypes.Type(value = Banana.class, name = "banana")})
class Fruit {
    public String name;
    public int pieces;
}

class Banana extends Fruit {
    public String origin;
}

Note that it will change the generated JSON to:

{
  "description": "My fruits",
  "fruits": [
    {
      "type": "fruit",
      "name": "apple",
      "pieces": 8
    },
    {
      "type": "banana",
      "name": "banana",
      "pieces": 5,
      "origin": "Ecuador"
    }
  ]
}

Note that this JSON now has an extra field type that will be used by Jackson to identify what sub-class need to be used to deserialize JSON.

You can change this property name as well as how Jackson include the extra information into the JSON. You can read more about it here: http://fasterxml.github.io/jackson-annotations/javadoc/2.4/com/fasterxml/jackson/annotation/JsonTypeInfo.html?is-external=true

Ilya Ovesnov
  • 4,079
  • 1
  • 27
  • 31
  • Thanks for your answer! Yes, you are right, I use `codehaus` and not `fasterxml`. The exact JSON version is project standard; I need to check if it is possible to change. I need to check if your solution works in our case. I'm afraid not, because that would mean an interface change, which should be avoided. (My client is a test tool, and the main client is happy with the current solution.) – Csaba Faragó Apr 20 '16 at 07:56
  • It turned out that there are many other problems which prohibit to parse the JSON fully automatically. If the modification of the server code would be a real option, your answer would solve the problem. Therefore I accept it as solution. – Csaba Faragó Apr 20 '16 at 10:41