0

Hi guys i'am with a problem with some bad JSON structure from my client server, my client can't change and i have handle in this side.

I'am creating POJO from JSON and I've already tried with GSON and now i'am using JACKSON because i thought the JACKSON TREE will help me.

The problem it's the server it's returning an object (extension) when it's only one and returning an ArrayList when are several. I know it's a bad structure why not return an array with 1 object but i cant do anything.

In my POJO class i want to get always a ArrayList in another words if it's only one add to an array else do nothing.

The JSON Example:

    {
  "result": {
    "FeatureCollection": {
      "boundedBy": {
        "Envelope": {
          "lowerCorner": "-DUMMIE",
          "srsName": "DUMMIE",
          "upperCorner": "DUMMIE"
        }
      },
      "featureMember": [
        {
          "Spill": {
            "DUMMIE": "DUMMIE",
            "DUMMIE": "DUMMIE",
            "DUMMIE": "DUMMIE",
            "extension": {
              "Extension": {
                "area": 401994.1,
                "width": 288.6,
                "DUMMIE": "DUMMIE",
                "length": 2797,
              }
            },
            "DUMMIE": "DUMMIE",
            "DUMMIE": "DUMMIE",
          }
        },
        {
          "Spill": {
            "DUMMIE": "DUMMIE",
            "DUMMIE": "DUMMIE",
            "DUMMIE": "DUMMIE",
            "extension": [
              {
                "Extension": {
                  "area": 401994.1,
                  "width": 288.6,
                  "DUMMIE": "DUMMIE",
                  "length": 2797,
                }
              },
              {
                "Extension": {
                  "area": 401994.1,
                  "width": 288.6,
                  "DUMMIE": "DUMMIE",
                  "length": 2797,
                }
              }
            ],
            "DUMMIE": "DUMMIE",
            "DUMMIE": "DUMMIE",
          }
        }
      ],
      "xsi:schemaLocation": "DUMMIE",
      "numberOfFeatures": 10
    }
  },
  "status": "success"
}

The POJO class where it's the problem

public class Spill {

@JsonProperty("dataSource")
private String dataSource;
@JsonProperty("timeStamp")
private String timeStamp;
@JsonProperty("SARWind_windIntensity")
private Double SARWindWindIntensity;
@JsonProperty("reliabilityIndex")
private Double reliabilityIndex;
@JsonProperty("meteoWind_dataType")
private String meteoWindDataType;
@JsonProperty("eventid")
private String eventid;
@JsonProperty("center")
private Center center;
@JsonProperty("area")
private Double area;
@JsonProperty("description")
private String description;
@JsonProperty("SARWind_windDirection")
private Long SARWindWindDirection;
@JsonProperty("name")
private String name;
@JsonProperty("meteoWind_dataSource")
private String meteoWindDataSource;
@JsonProperty("length")
private Double length;
@JsonProperty("meteoWind_windIntensity")
private Double meteoWindWindIntensity;
@JsonProperty("SARWind_dataSource")
private String SARWindDataSource;
@JsonProperty("SARWind_dataType")
private String SARWindDataType;
@JsonProperty("meteoWind_windDirection")
private Long meteoWindWindDirection;
@JsonProperty("imageIdentifier")
private ImageIdentifier imageIdentifier;
@JsonProperty("width")
private Double width;
@JsonProperty("origin")
private String origin;
@JsonProperty("extension")
private List<Extension> extension = new ArrayList<Extension>();
@JsonProperty("boundedBy")
private BoundedBy__ boundedBy;
@JsonProperty("auxiliaryDataRef")
private AuxiliaryDataRef auxiliaryDataRef;
@JsonProperty("gml:id")
private String gmlId;
@JsonProperty("alignedWithTrack")
private Boolean alignedWithTrack;
@JsonProperty("distanceFromCoast")
private Long distanceFromCoast;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();

/**
 * 
 * @return The dataSource
 */
@JsonProperty("dataSource")
public String getDataSource() {
    return dataSource;
}

/**
 * 
 * @param dataSource
 *            The dataSource
 */
@JsonProperty("dataSource")
public void setDataSource(String dataSource) {
    this.dataSource = dataSource;
}

/**
 * 
 * @return The timeStamp
 */
@JsonProperty("timeStamp")
public String getTimeStamp() {
    return timeStamp;
}

/**
 * 
 * @param timeStamp
 *            The timeStamp
 */
@JsonProperty("timeStamp")
public void setTimeStamp(String timeStamp) {
    this.timeStamp = timeStamp;
}

/**
 * 
 * @return The SARWindWindIntensity
 */
@JsonProperty("SARWind_windIntensity")
public Double getSARWindWindIntensity() {
    return SARWindWindIntensity;
}

/**
 * 
 * @param SARWindWindIntensity
 *            The SARWind_windIntensity
 */
@JsonProperty("SARWind_windIntensity")
public void setSARWindWindIntensity(Double SARWindWindIntensity) {
    this.SARWindWindIntensity = SARWindWindIntensity;
}

/**
 * 
 * @return The reliabilityIndex
 */
@JsonProperty("reliabilityIndex")
public Double getReliabilityIndex() {
    return reliabilityIndex;
}

/**
 * 
 * @param reliabilityIndex
 *            The reliabilityIndex
 */
@JsonProperty("reliabilityIndex")
public void setReliabilityIndex(Double reliabilityIndex) {
    this.reliabilityIndex = reliabilityIndex;
}

/**
 * 
 * @return The meteoWindDataType
 */
@JsonProperty("meteoWind_dataType")
public String getMeteoWindDataType() {
    return meteoWindDataType;
}

/**
 * 
 * @param meteoWindDataType
 *            The meteoWind_dataType
 */
@JsonProperty("meteoWind_dataType")
public void setMeteoWindDataType(String meteoWindDataType) {
    this.meteoWindDataType = meteoWindDataType;
}

/**
 * 
 * @return The eventid
 */
@JsonProperty("eventid")
public String getEventid() {
    return eventid;
}

/**
 * 
 * @param eventid
 *            The eventid
 */
@JsonProperty("eventid")
public void setEventid(String eventid) {
    this.eventid = eventid;
}

/**
 * 
 * @return The center
 */
@JsonProperty("center")
public Center getCenter() {
    return center;
}

/**
 * 
 * @param center
 *            The center
 */
@JsonProperty("center")
public void setCenter(Center center) {
    this.center = center;
}

/**
 * 
 * @return The area
 */
@JsonProperty("area")
public Double getArea() {
    return area;
}

/**
 * 
 * @param area
 *            The area
 */
@JsonProperty("area")
public void setArea(Double area) {
    this.area = area;
}

/**
 * 
 * @return The description
 */
@JsonProperty("description")
public String getDescription() {
    return description;
}

/**
 * 
 * @param description
 *            The description
 */
@JsonProperty("description")
public void setDescription(String description) {
    this.description = description;
}

/**
 * 
 * @return The SARWindWindDirection
 */
@JsonProperty("SARWind_windDirection")
public Long getSARWindWindDirection() {
    return SARWindWindDirection;
}

/**
 * 
 * @param SARWindWindDirection
 *            The SARWind_windDirection
 */
@JsonProperty("SARWind_windDirection")
public void setSARWindWindDirection(Long SARWindWindDirection) {
    this.SARWindWindDirection = SARWindWindDirection;
}

/**
 * 
 * @return The name
 */
@JsonProperty("name")
public String getName() {
    return name;
}

/**
 * 
 * @param name
 *            The name
 */
@JsonProperty("name")
public void setName(String name) {
    this.name = name;
}

/**
 * 
 * @return The meteoWindDataSource
 */
@JsonProperty("meteoWind_dataSource")
public String getMeteoWindDataSource() {
    return meteoWindDataSource;
}

/**
 * 
 * @param meteoWindDataSource
 *            The meteoWind_dataSource
 */
@JsonProperty("meteoWind_dataSource")
public void setMeteoWindDataSource(String meteoWindDataSource) {
    this.meteoWindDataSource = meteoWindDataSource;
}

/**
 * 
 * @return The length
 */
@JsonProperty("length")
public Double getLength() {
    return length;
}

/**
 * 
 * @param length
 *            The length
 */
@JsonProperty("length")
public void setLength(Double length) {
    this.length = length;
}

/**
 * 
 * @return The meteoWindWindIntensity
 */
@JsonProperty("meteoWind_windIntensity")
public Double getMeteoWindWindIntensity() {
    return meteoWindWindIntensity;
}

/**
 * 
 * @param meteoWindWindIntensity
 *            The meteoWind_windIntensity
 */
@JsonProperty("meteoWind_windIntensity")
public void setMeteoWindWindIntensity(Double meteoWindWindIntensity) {
    this.meteoWindWindIntensity = meteoWindWindIntensity;
}

/**
 * 
 * @return The SARWindDataSource
 */
@JsonProperty("SARWind_dataSource")
public String getSARWindDataSource() {
    return SARWindDataSource;
}

/**
 * 
 * @param SARWindDataSource
 *            The SARWind_dataSource
 */
@JsonProperty("SARWind_dataSource")
public void setSARWindDataSource(String SARWindDataSource) {
    this.SARWindDataSource = SARWindDataSource;
}

/**
 * 
 * @return The SARWindDataType
 */
@JsonProperty("SARWind_dataType")
public String getSARWindDataType() {
    return SARWindDataType;
}

/**
 * 
 * @param SARWindDataType
 *            The SARWind_dataType
 */
@JsonProperty("SARWind_dataType")
public void setSARWindDataType(String SARWindDataType) {
    this.SARWindDataType = SARWindDataType;
}

/**
 * 
 * @return The meteoWindWindDirection
 */
@JsonProperty("meteoWind_windDirection")
public Long getMeteoWindWindDirection() {
    return meteoWindWindDirection;
}

/**
 * 
 * @param meteoWindWindDirection
 *            The meteoWind_windDirection
 */
@JsonProperty("meteoWind_windDirection")
public void setMeteoWindWindDirection(Long meteoWindWindDirection) {
    this.meteoWindWindDirection = meteoWindWindDirection;
}

/**
 * 
 * @return The imageIdentifier
 */
@JsonProperty("imageIdentifier")
public ImageIdentifier getImageIdentifier() {
    return imageIdentifier;
}

/**
 * 
 * @param imageIdentifier
 *            The imageIdentifier
 */
@JsonProperty("imageIdentifier")
public void setImageIdentifier(ImageIdentifier imageIdentifier) {
    this.imageIdentifier = imageIdentifier;
}

/**
 * 
 * @return The width
 */
@JsonProperty("width")
public Double getWidth() {
    return width;
}

/**
 * 
 * @param width
 *            The width
 */
@JsonProperty("width")
public void setWidth(Double width) {
    this.width = width;
}

/**
 * 
 * @return The origin
 */
@JsonProperty("origin")
public String getOrigin() {
    return origin;
}

/**
 * 
 * @param origin
 *            The origin
 */
@JsonProperty("origin")
public void setOrigin(String origin) {
    this.origin = origin;
}

/**
 * 
 * @return The extension
 */
@JsonProperty("extension")
public List<Extension> getExtension() {
    return extension;
}

/**
 * 
 * @param extension
 *            The extension
 */
@JsonProperty("extension")
public void setExtension(List<Extension> extension) {
    this.extension = extension;
}

/**
 * 
 * @return The boundedBy
 */
@JsonProperty("boundedBy")
public BoundedBy__ getBoundedBy() {
    return boundedBy;
}

/**
 * 
 * @param boundedBy
 *            The boundedBy
 */
@JsonProperty("boundedBy")
public void setBoundedBy(BoundedBy__ boundedBy) {
    this.boundedBy = boundedBy;
}

/**
 * 
 * @return The auxiliaryDataRef
 */
@JsonProperty("auxiliaryDataRef")
public AuxiliaryDataRef getAuxiliaryDataRef() {
    return auxiliaryDataRef;
}

/**
 * 
 * @param auxiliaryDataRef
 *            The auxiliaryDataRef
 */
@JsonProperty("auxiliaryDataRef")
public void setAuxiliaryDataRef(AuxiliaryDataRef auxiliaryDataRef) {
    this.auxiliaryDataRef = auxiliaryDataRef;
}

/**
 * 
 * @return The gmlId
 */
@JsonProperty("gml:id")
public String getGmlId() {
    return gmlId;
}

/**
 * 
 * @param gmlId
 *            The gml:id
 */
@JsonProperty("gml:id")
public void setGmlId(String gmlId) {
    this.gmlId = gmlId;
}

/**
 * 
 * @return The alignedWithTrack
 */
@JsonProperty("alignedWithTrack")
public Boolean getAlignedWithTrack() {
    return alignedWithTrack;
}

/**
 * 
 * @param alignedWithTrack
 *            The alignedWithTrack
 */
@JsonProperty("alignedWithTrack")
public void setAlignedWithTrack(Boolean alignedWithTrack) {
    this.alignedWithTrack = alignedWithTrack;
}

/**
 * 
 * @return The distanceFromCoast
 */
@JsonProperty("distanceFromCoast")
public Long getDistanceFromCoast() {
    return distanceFromCoast;
}

/**
 * 
 * @param distanceFromCoast
 *            The distanceFromCoast
 */
@JsonProperty("distanceFromCoast")
public void setDistanceFromCoast(Long distanceFromCoast) {
    this.distanceFromCoast = distanceFromCoast;
}

@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
    return this.additionalProperties;
}

@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
    this.additionalProperties.put(name, value);
}

}

what i've tried: GSON:

CnsDetection cnsDetection = gson.fromJson(json, CnsDetection.class);

JACKSON

    ObjectMapper mapper = new ObjectMapper();
cnsDetection = mapper.readValue(json, CnsDetection.class);

Tried using a custom Deserializer but no luck. My main problem it's how i will change the specific node from object to arrayList and after that how i will creat the CnsDetection Object witch contains the Spill object.

I've also read: - How to dynamically handle json response array/object using Gson - GSON Expected BEGIN_ARRAY but was BEGIN_OBJECT

UPDATE

I've tried the @vzamanillo answer but i was getting several errors, but is answer put me in the right path and the correct answer to my question. PS:as he asked here it's my ExtensionDeserializer:

NOTE: The jacksonHelper it's a singleton so i can reuse the mapper in the app.

NOT WORKING Deserializer

public class ExtensionDeserializer extends JsonDeserializer<List<Extension>> {
JacksonHelper jacksonHelper = JacksonHelper.getInstance();

@Override
public List<Extension> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
    ObjectCodec oc = jsonParser.getCodec();
    JsonNode node = oc.readTree(jsonParser);
    List<Extension> result = new ArrayList<Extension>();

    if (node.isObject()) {
        result =
                jacksonHelper.getMapper().convertValue(node,
                        jacksonHelper.getMapper().getTypeFactory().constructCollectionType(List.class, Extension.class));
    }
    if (node.isArray()) {
        result.add(jsonParser.readValueAs(Extension.class));
    }
    return result;
}}

WORKING Deserializer credits to:@vzamanillo

public class ExtensionDeserializer extends JsonDeserializer<List<Extension>> {
JacksonHelper jacksonHelper = JacksonHelper.getInstance();

@Override
public List<Extension> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
    ObjectCodec oc = jsonParser.getCodec();
    JsonNode node = oc.readTree(jsonParser);
    List<Extension> result = new ArrayList<Extension>();

    if (node.isObject()) {
        result.add(jacksonHelper.getMapper().treeToValue(node, Extension.class));
    }
    if (node.isArray()) {
        result = jacksonHelper.getMapper().readValue(node.traverse(), new TypeReference<List<Extension>>() {
        });
    }
    return result;
}}
Community
  • 1
  • 1
firetrap
  • 1,947
  • 3
  • 24
  • 39
  • Nice solution when the node is an object, better than mine :), glad to help you. – vzamanillo May 13 '15 at 10:04
  • That's why we are one of the best community, everyone can help each other and learn. In your answer you have added "my solution" in "node.isObject()" but that it's in the "node.isArray()" just to avoid confusions ;) – firetrap May 13 '15 at 10:11
  • ouch! :), updated answer – vzamanillo May 13 '15 at 10:18

3 Answers3

3

Create a custom JsonDeserializer for extension property and check if the node type is an Array or an Object, if the node is an object add it to the returned List.

    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;

    import org.codehaus.jackson.JsonNode;
    import org.codehaus.jackson.JsonParser;
    import org.codehaus.jackson.ObjectCodec;
    import org.codehaus.jackson.map.DeserializationContext;
    import org.codehaus.jackson.map.JsonDeserializer;
    import org.codehaus.jackson.map.ObjectMapper;

    class ExtensionDeSerializer extends JsonDeserializer<List<Extension>> {
        @Override
        public List<Extension> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
            ObjectCodec oc = jsonParser.getCodec();
            JsonNode node = oc.readTree(jsonParser);
            List<Extension> result = new ArrayList<Extension>();
            if (node.isObject()) {

                ObjectMapper mapper = new ObjectMapper();
                // @firetrap less obscure and better solution
                //result.add(jacksonHelper.getMapper().treeToValue(node, Extension.class));

                result = mapper.convertValue(node, mapper.getTypeFactory().constructCollectionType(List.class, Extension.class));
            }
            if (node.isArray()) {
                result.add(jsonParser.readValueAs(Extension.class));
            }
            return result;
        }
    }

Add the @JsonDeserialize annotation to the extension property in your POJO class.

@JsonDeserialize(using=ExtensionDeSerializer.class)
@JsonProperty("extension")
private List<Extension> extension = new ArrayList<Extension>();

This is (probably) the most efficient way to solve the problem when a property can be an object or an array.

Hope this helps.

vzamanillo
  • 9,905
  • 1
  • 36
  • 56
  • I didn't try deserialize with JACKSON only with GSON but now looking your code I see I did some mistakes. Your code seems pretty well tomorrow I will give it a try and report back. Thanks. – firetrap May 12 '15 at 21:31
  • I tried your code but gives me error when node.isObject(): Can not deserialize instance of java.util.ArrayList out of START_OBJECT token at [Source: N/A; line: -1, column: -1] – firetrap May 12 '15 at 23:36
  • I changed your code and tried: Extension extension = mapper.treeToValue(node, Extension.class); result.add(extension); and it's ok when node.isObject() but the problem it's when it's when node.isArray() it gives: java.lang.NullPointerException: Attempt to invoke virtual method 'int com.fasterxml.jackson.core.JsonToken.ordinal()' on a null object reference – firetrap May 12 '15 at 23:38
  • My code works as expected with Java Jackson-asl 1.8.5, update your question to show us your Deserializer code. – vzamanillo May 13 '15 at 07:20
0

In the first part of your JSON structure the "extension" contains an object, however in the second instance you have an array of objects.

Even when you have 1 object if you may have multiple you should use an array throughout to keep the structure.

L. Young
  • 163
  • 3
  • 7
  • 24
  • Yes I know that's the problem that I am trying to solve, and changing the json response unfortunately it's not an option – firetrap May 12 '15 at 18:33
0

I think problem is with parsing "extension" array. Since it is also an array, you should create a new inner class for its content, inside your model class :

public static class Extension {
    @JsonProperty("area")
    private Double area;
    @JsonProperty("width")
    public String width;
    @JsonProperty("length")
    public String length;

And than generate its setters and getters. For being more clear on the point, I will paste a code piece that i used for following case with JSON:

{
"table_name":"bbbb",
"table_version":1,
"table_data":[
             {
              "OPERATION":"aaaa",
              "TRANSACTION_ID":1
             }
             ]
}

And for parsing that kind of a JSON i used the model with the piece i pasted above; with table_date in my case being inner class.

Regards,

denizt
  • 713
  • 5
  • 21
  • Are you suggesting to create a wrapper class? If it's the wrapper I can't see how it will solve and I've already tried. Yes the problem it's with the extension which sometimes it's an array and others it's an object, tomorrow I will update my question with the full json and the full Pojo classes – firetrap May 12 '15 at 20:11