JJD
JJD

Reputation: 51824

How to parse nested arrays with Jackson?

I used to successfully parse the following .json file:

[
    {
        "latitude": 49.419459253939316,
        "longitude": 8.676411621072491
    },
    {
        "latitude": 49.41946061080915,
        "longitude": 8.676411644939083
    },
    {
        "latitude": 49.420365910782735,
        "longitude": 8.676438042403413
    }
]

The following Jackson script outputs a List of points.

private static <T> List<T> parseFile(final String fileName, 
                                     Class<T> contentType) {
    // ...
    InputStream inputStream = // Open file
    ObjectMapper objectMapper = new ObjectMapper();
    try {
        TypeFactory typeFactory = objectMapper.getTypeFactory();
        CollectionType collectionType = typeFactory
            .constructCollectionType(List.class, contentType);
        return objectMapper.readValue(inputStream, collectionType);
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

Now the dataset gets more complicated. The List of points becomes a List of List of points. I structured it this way - please correct me if this is not correct.

[
    [
        {
            "latitude": 49.419459253939316,
            "longitude": 8.676411621072491
        },
        {
            "latitude": 49.41946061080915,
            "longitude": 8.676411644939083
        },
        {
            "latitude": 49.420365910782735,
            "longitude": 8.676438042403413
        }
    ],
    [
        {
            "latitude": 49.40460334213399,
            "longitude": 8.670034018853409
        },
        {
            "latitude": 49.404608057285145,
            "longitude": 8.670028775634165
        },
        {
            "latitude": 49.40506145685422,
            "longitude": 8.66955817506422
        }
    ]
]

I prepared the following POJOs to store the data into:

public class GeoPoint {
    public double latitude;
    public double longitude;
}

...

public class ThreePoints {
    public List<GeoPoint> points;  
}

How do I have to change the above Jackson parser so it can handle the nested arrays? Can Jackson parse the data into a nested class structure such as the ThreePoints.class?

Upvotes: 1

Views: 5534

Answers (4)

ewert
ewert

Reputation: 1825

In my opinion the most elegant solution would be to use TypeReference

ObjectMapper mapper = new ObjectMapper();
TypeReference<List<List<GeoPoint>>> typeRef = new TypeReference<List<List<GeoPoint>>>() {};
List<List<GeoPoint>> locations = mapper.readValue(jsonAsString, typeRef);

Upvotes: 1

hanung
hanung

Reputation: 358

You can write a simple custom deserializer

To deserialize it to your class:

public class GeoPoint {
    public double latitude;
    public double longitude;
}

public class ThreePoints {
    public List<GeoPoint> points;
}

Write a custom deserializer:

class ThreePointsDeserializer extends StdDeserializer<ThreePoints> {

    protected ThreePointsDeserializer() {
        super(ThreePoints.class);
    }


    @Override
    public ThreePoints deserialize(JsonParser parser, DeserializationContext ctx)
            throws IOException, JsonProcessingException {
        ThreePoints result = new ThreePoints();
        GeoPoint[] points = parser.getCodec().readValue(parser, GeoPoint[].class);
        result.points = Arrays.asList(points);
        return result;
    }
}

to use that deserializer:

SimpleModule module = new SimpleModule();
module.addDeserializer(ThreePoints.class, new ThreePointsDeserializer());

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);

TypeFactory tf = mapper.getTypeFactory();
CollectionType collectionType = tf.constructCollectionType(List.class, ThreePoints.class);

List<ThreePoints> result = mapper.readValue(YOUR_DATA, collectionType);

Upvotes: 1

Michał Ziober
Michał Ziober

Reputation: 38655

You can simply create additional collection type. See below code:

TypeFactory typeFactory = mapper.getTypeFactory();
CollectionType listType = typeFactory.constructCollectionType(List.class, contentType);
CollectionType listListType = typeFactory.constructCollectionType(List.class, listType);
List<List<GeoPoint>> readValue = mapper.readValue(json, rootCollectionType);
// convert to ThreePoints

EDIT
Unfortunately you are not able to tell Jackson to convert your JSON to your POJO classes because they do not fit to each other. Jackson also does not contain annotations which you can use to map your JSON to your POJO classes. You have to do it manually. Using my code you can write custom deserializer for ThreePoints class in which you can use inner ObjectMapper and my above code. I think, that you will be able easily convert List<List<GeoPoint>> readValue to ThreePoints class. Another option - you can write just a simple function in you JsonUtil class. If you really can not change this JSON you have to do it manually.

Upvotes: 3

Ben Rhouma Zied
Ben Rhouma Zied

Reputation: 2603

you need to read the values as Matrix, first you need to map the values using a Pojo called Place.

public class Place {

    double latitude;
    double longitude;

    public double getLatitude() {
        return latitude;
    }

    public void setLatitude(double latitude) {
        this.latitude = latitude;
    }

    public double getLongitude() {
        return longitude;
    }

    public void setLongitude(double longitude) {
        this.longitude = longitude;
    }
}       

Second step, you need to map the JsonNode into a Matrix.

import java.io.IOException;
import java.util.List;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.type.CollectionType;
import org.codehaus.jackson.map.type.TypeFactory;


public class Main {
   static String jsonString =  "[" +
"    [" +
"        {" +
"            \"latitude\": 49.419459253939316," +
"            \"longitude\": 8.676411621072491" +
"        }," +
"        {" +
"            \"latitude\": 49.41946061080915," +
"            \"longitude\": 8.676411644939083" +
"        }," +
"        {" +
"            \"latitude\": 49.420365910782735," +
"            \"longitude\": 8.676438042403413" +
"        }" +
"    ]," +
"    [" +
"        {" +
"            \"latitude\": 49.40460334213399," +
"            \"longitude\": 8.670034018853409" +
"        }," +
"        {" +
"            \"latitude\": 49.404608057285145," +
"            \"longitude\": 8.670028775634165" +
"        }," +
"        {" +
"            \"latitude\": 49.40506145685422," +
"            \"longitude\": 8.66955817506422" +
"        }" +
"    ]" +
"]";

    public static void main(String...args) {
        final ObjectMapper om = new ObjectMapper();
        try {
           final Place[][] dtos = om.readValue(new ObjectMapper()
               .readValue(jsonString, JsonNode.class), Place[][].class);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Cheers.

Upvotes: 0

Related Questions