I'm glad the solution worked! Let's break down what happened and why it worked, which will help you understand the entire Spring Boot + Hibernate Spatial + GeoJSON + PostGIS stack better.
You were experiencing an issue where your Point geometry field (cityCoordinates
) wasn't being serialized correctly to JSON. Instead of getting a proper GeoJSON Point representation, you were getting a deeply nested structure with repeated "envelope" objects:
"cityCoordinates": {
"envelope": {
"envelope": {
"envelope": {
// more nesting...
This happened because of how the serialization process works in Spring Boot and how geometry objects are structured:
-
JPA/Hibernate Side: Hibernate Spatial correctly maps your database's PostGIS
geography(Point,4326)
type to the JTSPoint
Java object. -
Serialization Side: When Spring tries to convert your entities to JSON (using Jackson), it doesn't know how to handle the complex
Point
object from the JTS (Java Topology Suite) library. -
Default Behavior: Without guidance, Jackson tries to serialize all fields and methods of the
Point
object, including internal implementation details like envelope boundaries, which leads to the deeply nested structure you saw.
The solution that worked has two key parts:
@JsonIgnore
@Column(name = "city_coordinates", nullable = false, columnDefinition = "geography(Point,4326)")
private Point cityCoordinates;
This annotation tells Jackson: "Don't try to serialize this field at all." This prevents the problematic serialization of the complex JTS Point
object.
@JsonProperty("cityCoordinates")
public Map<String, Object> getCityCoordinatesJson() {
if (this.cityCoordinates == null) {
return null;
}
Map<String, Object> geoJson = new HashMap<>();
geoJson.put("type", "Point");
geoJson.put("coordinates", new double[] {
this.cityCoordinates.getX(), // longitude
this.cityCoordinates.getY() // latitude
});
return geoJson;
}
This method:
- Is annotated with
@JsonProperty("cityCoordinates")
which tells Jackson: "When serializing, use this method's return value for the 'cityCoordinates' property" - Manually creates a Map structure that follows the GeoJSON format for Point objects
- Extracts just the X (longitude) and Y (latitude) values from the JTS Point object
- Returns a simple Map with arrays that Jackson knows how to serialize properly
PostGIS extends PostgreSQL with geospatial capabilities. The geography(Point,4326)
type:
geography
: Stores coordinates on a spheroid (Earth's surface), accounting for curvaturePoint
: The geometry type (as opposed to LineString, Polygon, etc.)4326
: The SRID (Spatial Reference ID) for WGS84, the standard coordinate system used in GPS
Hibernate Spatial is the bridge between your Java application and the PostGIS database:
- It translates between Java objects and PostGIS types
- The JTS
Point
class represents a point in two-dimensional space - It provides methods like
getX()
andgetY()
to access coordinates
Spring Boot uses Jackson for JSON serialization/deserialization:
- By default, it tries to convert all properties of your entities to JSON
- It doesn't know how to handle specialized objects like JTS
Point
- Annotations like
@JsonIgnore
and@JsonProperty
control this process
GeoJSON is a standard format for encoding geographic data structures:
- For a Point, it looks like:
{"type": "Point", "coordinates": [longitude, latitude]}
- The coordinates array has longitude first, then latitude (x, y)
- This is the format expected by most mapping libraries (like Leaflet, Mapbox, etc.)
The other solutions (custom Jackson serializers or modules) didn't work likely because:
- They might not have been properly registered with Spring Boot
- They might not have been compatible with your specific JTS version
- There could have been conflicts with other Jackson configurations
The solution that worked is more direct—it bypasses Jackson's complex object serialization entirely and instead provides a simple structure that Jackson can easily convert to JSON.
This approach (using @JsonIgnore
+ custom getter) is particularly useful when:
- You need to transform complex objects into a specific JSON format
- You want to avoid adding extra libraries or configurations
- You prefer to keep the transformation logic within your entity class
For larger projects, you might eventually want to separate these concerns by using DTOs (Data Transfer Objects) or dedicated serializers, but for your immediate needs, this approach works very well and is straightforward to understand.