9

I am trying to deserialize some JSON to a class using JSON.deserialize. My JSON looks like this

{
    "type": "GeometryCollection",
    "geometries": [
        {
            "type": "Point",
            "coordinates": [-3.529138029308078, 50.71957856160531]
        }, 
        {
            "type": "Polygon",
            "coordinates": [
                [
                    [-3.5381984710693364, 50.72477461305143],
                    [-3.5381984710693364, 50.727165282893836],
                    [-3.5295295715332036, 50.727165282893836],
                    [-3.5295295715332036, 50.72477461305143],
                    [-3.5381984710693364, 50.72477461305143]
                ]
            ]
        }
    ]
}

and the class I am trying to use looks like this

public class geometryCollection 
{
  String type;   
  List<geometry> geometries; 
}

public class geometry 
{
  String type;
  /*List<Decimal> coordinates;
  List<List<List<Decimal>>> coordinates;*/
}

As you can see in my JSON the items in the 'geometries' array have a type and coordinates. The problem is that for a point the coordinates are of type List<Decimal> and for polygon they are of type List<List<List<Decimal>>>.

Is there a good way to deserialize this JSON when two different data types use the same name?

3
  • 2
    Quick tip: Whenever you need to use angle brackets, you should enclose them in backticks. Otherwise, SFSE will treat it like html and not render the contents.
    – Derek F
    Commented May 18, 2018 at 15:16
  • I don't suppose that modifying the source of this JSON is an option, is it?
    – Derek F
    Commented May 18, 2018 at 15:22
  • Not at the source but actually I could do a find and replace on the string in apex. Replace the "coordinates:" for a polygon with "polygonCoordinates:" and then update the class.
    – Sam Maton
    Commented May 18, 2018 at 15:24

2 Answers 2

4

So I decided the easiest way is to do a find and replace on the JSON string and change the names of one of the duplicate variables.

String s2 = geoJSON.replace('"coordinates":[[[', '"polygonCoordinates":[[[');

Then I updated my classes to look like this

public class geometryCollection
{
   String type;   
   List<geometry> geometries; 
}

public class geometry
{
   String type;
   List<Decimal> coordinates;
   List<List<List<Decimal>>> polygonCoordinates;
}

Then once I have serialized the JOSN I can change it back.

1
  • 1
    Note that a regex would be safer as there may be whitespace between the left hand square braces. Personally instead I would use JSON.deserializeUntyped and build the Apex typed objects myself: instanceof can be used to check the types of values.
    – Keith C
    Commented May 18, 2018 at 17:12
0

maton, anyone who encounters this use case,

I had a similar use case. I ended up opensourcing my take on this https://github.com/gaelmotte/apex-json-serialization

After deploying, your code can be adapted as such:

public class GeometryCollection 
{
  String type;   
  List<Geometry> Geometries; 
}

public abstract class Geometry implements JSONDeserializer.Deserializable, JSONDeserializer.Polymorph
{
  String type;
   public virtual void setDeserializedField(String key, Object value) {
      switch on key {
        when 'type' {
          this.type = (String) value;
        }
      }
    }
    public virtual Map<String, System.Type> getDeserializableFields() {
      return new Map<String, System.Type>{ 'type' => String.class };
    }
}

public class GeometryDiscriminator implements JSONDeserializer.Discriminator {
    public System.Type discriminate(Map<String, Object> untyppedMap) {
      if (untyppedMap.get('type') == 'Point') {
        return PointGeometry.class;
      }
      return PolygonGeometry.class;
    }
  }

public PointGeometry extends Geometry {
  List<Decimal> coordinates;
 public override void setDeserializedField(String key, Object value) {
      switch on key {
        when 'coordinates' {
          this.coordinates = (List<Decimal>) value;
        }
        when else {
          super.setDeserializedField(key, value);
        }
      }
    }
    public override Map<String, System.Type> getDeserializableFields() {
      Map<String, System.Type> fields = super.getDeserializableFields();
      fields.putAll(new Map<String, System.Type>{ 'coordinates' =>  List<Decimal>.class });
      return fields;
    }
}

public PolygonGeometry extends Geometry {
  List<List<List<Decimal>>> coordinates;
 public override void setDeserializedField(String key, Object value) {
      switch on key {
        when 'coordinates' {
          this.coordinates = (List<List<List<Decimal>>>) value;
        }
        when else {
          super.setDeserializedField(key, value);
        }
      }
    }
    public override Map<String, System.Type> getDeserializableFields() {
      Map<String, System.Type> fields = super.getDeserializableFields();
      fields.putAll(new Map<String, System.Type>{ 'coordinates' =>  List<List<List<Decimal>>>.class });
      return fields;
    }
}

I agree this is more tedious to setup, but much cleaner (polymorphism may benefit you in the long run). I wish there were some kind of custom annotations or introspection, but too bad...

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .