Skip to content

[Detail Bug] Apple Maps API: Deserialization drops location and route step index fields due to missing JSON property aliasesΒ #66

@detail-app

Description

@detail-app

Detail Bug Report

https://app.detail.dev/org_befd6425-a158-4e24-9d4d-1e5c08769515/bugs/bug_85cf9ef9-346c-4ecd-8328-ae91cb2bf7ee

Summary

  • Context: AppleMapsObjectMapperFactory is responsible for creating and configuring a Jackson ObjectMapper to correctly deserialize Apple Maps Server API responses into domain model records.
  • Bug: The factory is missing necessary property mappings for several domain models where the API's property names differ from the record component names, such as AutocompleteResult.location (API: coordinate) and DirectionsRoute.stepIndexes (API: stepIndices).
  • Actual vs. expected: Deserialization results in empty or missing values for these fields because Jackson cannot find a matching property name in the JSON, even though the data is present under a different name (e.g., coordinate instead of location).
  • Impact: Critical location data and route information are lost during deserialization, causing these fields to appear empty in the domain models even when the API provided them.

Code with Bug

public static ObjectMapper create() {
    return JsonMapper.builder()
        .addMixIn(Location.class, LocationMixin.class) // <-- BUG πŸ”΄ Missing mixins for AutocompleteResult and DirectionsRoute
        .changeDefaultPropertyInclusion(inclusion ->
            inclusion.withValueInclusion(JsonInclude.Include.NON_NULL) // <-- BUG πŸ”΄ Should use NON_ABSENT to correctly handle Optional.empty()
        )
        .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
        .enable(EnumFeature.READ_ENUMS_USING_TO_STRING)
        .enable(EnumFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)
        .enable(EnumFeature.WRITE_ENUMS_USING_TO_STRING)
        .build();
}

Explanation

Because FAIL_ON_UNKNOWN_PROPERTIES is disabled, when the API sends fields whose names don’t match the record component names (e.g. JSON coordinate vs record location, JSON stepIndices vs record stepIndexes), Jackson silently leaves the record components at their default values (Optional.empty(), empty list). The factory currently registers only LocationMixin, so these aliases are never applied for the affected models.

Separately, using JsonInclude.Include.NON_NULL does not omit absent Optional values; Optional.empty() may serialize as null rather than being omitted. NON_ABSENT is the inclusion policy intended to omit absent Optional values.

Failing Test

@Test
void autocompleteResultDeserialization() throws Exception {
    ObjectMapper objectMapper = AppleMapsObjectMapperFactory.create();
    String json = """
        {
          "completionUrl": "https://maps.apple.com/...",
          "displayLines": ["Line 1"],
          "coordinate": { "lat": 37.7, "lng": -122.4 }
        }
        """;
    
    AutocompleteResult result = objectMapper.readValue(json, AutocompleteResult.class);
    // This fails: result.location() is Optional.empty()
    org.junit.jupiter.api.Assertions.assertTrue(result.location().isPresent(), "Location should be present");
}

@Test
void directionsRouteDeserialization() throws Exception {
    ObjectMapper objectMapper = AppleMapsObjectMapperFactory.create();
    String json = """
        {
          "stepIndices": [0, 1, 2]
        }
        """;
    
    DirectionsRoute route = objectMapper.readValue(json, DirectionsRoute.class);
    // This fails: route.stepIndexes() is an empty list []
    org.junit.jupiter.api.Assertions.assertEquals(List.of(0, 1, 2), route.stepIndexes());
}

Recommended Fix

Add mixins for the affected models to provide @JsonAlias mappings, and switch inclusion to NON_ABSENT.

public static ObjectMapper create() {
    return JsonMapper.builder()
        .addMixIn(Location.class, LocationMixin.class)
        .addMixIn(AutocompleteResult.class, AutocompleteResultMixin.class) // <-- FIX 🟒
        .addMixIn(DirectionsRoute.class, DirectionsRouteMixin.class) // <-- FIX 🟒
        .changeDefaultPropertyInclusion(inclusion ->
            inclusion.withValueInclusion(JsonInclude.Include.NON_ABSENT) // <-- FIX 🟒
        )
        // ... rest of configuration
        .build();
}

abstract static class AutocompleteResultMixin {
    @JsonAlias("coordinate")
    public abstract Optional<Location> location();
}

abstract static class DirectionsRouteMixin {
    @JsonAlias("stepIndices")
    public abstract List<Integer> stepIndexes();
}

History

This bug was introduced in commit 3480a9e. The original change added the AppleMapsObjectMapperFactory to centralize Jackson configuration, but it failed to provide necessary property mappings for several domain models and used an incorrect inclusion policy for Optional fields.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions