2 Replies Latest reply on Dec 30, 2014 11:10 AM by csa

    How to remove additional wrappering from a nested object, for deserialization?

    pnakaska

      I have a parent object SearchCommand that has a nested Geometry class. The code creates a Polygon using a factory

       

      LinearRing ring = factory.createLinearRing(new Coordinate[]

          { new Coordinate(-117.76469603701167, 57.692329503799144),

              new Coordinate(-117.76469603701167, 55.14112833650245),

              new Coordinate(-121.58793822451068, 55.14112833650245),

              new Coordinate(-121.58793822451068, 57.692329503799144),

              new Coordinate(-117.76469603701167, 57.692329503799144) });

      Polygon createPolygon = factory.createPolygon(ring, null);

       

      When the createPolygon is Serialized it appears as:

       

      {"^ObjectID":"10", "^Value":[{"^ObjectID":"11", "^Value":[{"^ObjectID":"12", "^Value":[{"^ObjectID":"13", "^NumVal":-117.76469603701167},{"^ObjectID":"14", "^NumVal":57.692329503799144}]},{"^ObjectID":"15", "^Value":[{"^ObjectID":"16", "^NumVal":-117.76469603701167},{"^ObjectID":"17", "^NumVal":55.14112833650245}]},{"^ObjectID":"18", "^Value":[{"^ObjectID":"19", "^NumVal":-121.58793822451068},{"^ObjectID":"20", "^NumVal":55.14112833650245}]},{"^ObjectID":"21", "^Value":[{"^ObjectID":"22", "^NumVal":-121.58793822451068},{"^ObjectID":"23", "^NumVal":57.692329503799144}]},{"^ObjectID":"24", "^Value":[{"^ObjectID":"25", "^NumVal":-117.76469603701167},{"^ObjectID":"26", "^NumVal":57.692329503799144}]}]}]}

       

      The deserializer:

      public Geometry deserialize(EJValue o)

        {

          EJObject obj = o.isObject();

          String typeName = obj.get("type").isString().stringValue();

         ....

          if (typeName.equals("Polygon"))

          {

            return parsePolygonCoordinates( obj.get("coordinates"));

          }

          ...

      The obj.get("coordinates")

      is :

      EJValue ejValue = obj.get("coordinates");

      the ejValue should be an array or EJArray, as expected by the parsePolygonCoordinates, but ejValue.isArray() returns NULL.

       

      I think the additional wrappering is causing problems in the geometry Deserializer, when attempting to deserialize this array, the object.toArray() returns NULL,

      is there a way to strip out (or avoid) this additional wrappering (the ^ObjectID and ^Value wrappers) around the array?

        • 1. Re: How to remove additional wrappering from a nested object, for deserialization?
          pnakaska

          The Geometry class from com.vividsolutions.jts:

          (pom.xml)

             <dependency>

                <groupId>com.vividsolutions.jts</groupId>

                <artifactId>jts-gwt</artifactId>

                <version>1.14-SNAPSHOT</version>

                <scope>provided</scope>

              </dependency>

          <!-- and sources -->

              <dependency>

                <groupId>com.vividsolutions.jts</groupId>

                <artifactId>jts-gwt</artifactId>

                <version>1.14-SNAPSHOT</version>

                <classifier>sources</classifier>

                <scope>provided</scope>

              </dependency>

          in the Entrypoint, Jackson Marshalling is enabled:

          RestClient.setJacksonMarshallingActive(true);

           

          also in the POM, errai-jaxrs-provider has been removed (Jackson/Resteasy used server side)

           

          This issue relates to com.vividsolutions.jts.Geometry and it's nested classes, which are all Serializeable.

          The applicationn provides a search service  which responds to queries with a Geometry, containing the coordinates (Polygon).

           

          The GeometrySerializer

           

          public class GeometrySerializer

          {

            public String serialize(Geometry value)

            {

              if (value instanceof Polygon)

              {

                return writePolygon((Polygon)value);

              }

              else

              {

                throw new UnsupportedOperationException("Unsupported Geometry type: " + value.getClass().getName());

              }

            }

           

            private String writePolygon(Polygon value)

            {

              JSONObject obj = new JSONObject();

              obj.put("type", new JSONString("Polygon"));

              obj.put("coordinates", writePolygonCoordinates(value));

              return obj.toString();

            }

           

            private JSONValue writePolygonCoordinates(Polygon value)

            {

              JSONArray array = new JSONArray();

              array.set(0, writeLineStringCoords(value.getExteriorRing()));

              for (int i = 0; i != value.getNumInteriorRing(); ++i)

              {

                array.set(i, writeLineStringCoords(value.getInteriorRingN(i)));

              }

              return array;

            }

           

            private JSONArray writeLineStringCoords(LineString ring)

            {

              JSONArray array = new JSONArray();

              for (int i = 0; i != ring.getNumPoints(); ++i)

              {

                Point p = ring.getPointN(i);

                array.set(i, writePointCoords(p));

              }

              return array;

            }

           

            private JSONArray writePointCoords(Point p)

            {

              JSONArray array = new JSONArray();

              array.set(0, new JSONNumber(p.getX()));

              array.set(1, new JSONNumber(p.getY()));

              return array;

            }

          }

           

          The result string of the serializer call writePolygonCoordinates (on the server, before sent to client) for the test case is:

           

          {"type":"Polygon", "coordinates":[[[-117.76469603701167,57.692329503799144],[-117.76469603701167,55.14112833650245],[-121.58793822451068,55.14112833650245],[-121.58793822451068,57.692329503799144],[-117.76469603701167,57.692329503799144]]]}"

           

           

          on the client in the deserializer:

          public Geometry deserialize(EJValue o)

            {

              EJObject obj = o.isObject();

              String typeName = obj.get("type").isString().stringValue();

              if (typeName.equals("Polygon"))

              {

                     EJValue ejValue = obj.get("coordinates");

                     return parsePolygonCoordinates(ejValue.isArray());

              }

              else

              {

                throw new UnsupportedOperationException("Unsupported geometry type: " + typeName);

              }

            }

           

           

            private Polygon parsePolygonCoordinates(EJArray array)

            {

              return gf.createPolygon(

                  parseExteriorRing(array),

                  parseInteriorRings(array));

            }

           

           

            private LinearRing parseExteriorRing(EJArray array)

            {

              return gf.createLinearRing(parseLineString(array.get(0).isArray()));

            }

           

           

            private LinearRing[] parseInteriorRings(EJArray array)

            {

              LinearRing rings[] = new LinearRing[array.size() - 1];

              for (int i = 1; i < array.size(); ++i)

              {

                rings[i - 1] = gf.createLinearRing(parseLineString(array.get(i).isArray()));

              }

              return rings; 

            }

           

           

            private Coordinate[] parseLineString(EJArray array)

            {

              Coordinate[] points = new Coordinate[array.size()];

              for (int i = 0; i != array.size(); ++i)

              {

                points[i] = parseCoordinate(array.get(i).isArray());

              }

              return points;

            }

           

           

            private Coordinate parseCoordinate(EJArray coords)

            {

              return new Coordinate(

                  coords.get(0).isNumber().doubleValue(),

                  coords.get(1).isNumber().doubleValue());

            }

           

          The deserializer (client) can retrieve the coordinates off of the document, but it looks like this:

           

          {"^ObjectID":"10", "^Value":[{"^ObjectID":"11", "^Value":[{"^ObjectID":"12", "^Value":[{"^ObjectID":"13", "^NumVal":-117.76469603701167},{"^ObjectID":"14", "^NumVal":57.692329503799144}]},{"^ObjectID":"15", "^Value":[{"^ObjectID":"16", "^NumVal":-117.76469603701167},{"^ObjectID":"17", "^NumVal":55.14112833650245}]},{"^ObjectID":"18", "^Value":[{"^ObjectID":"19", "^NumVal":-121.58793822451068},{"^ObjectID":"20", "^NumVal":55.14112833650245}]},{"^ObjectID":"21", "^Value":[{"^ObjectID":"22", "^NumVal":-121.58793822451068},{"^ObjectID":"23", "^NumVal":57.692329503799144}]},{"^ObjectID":"24", "^Value":[{"^ObjectID":"25", "^NumVal":-117.76469603701167},{"^ObjectID":"26", "^NumVal":57.692329503799144}]}]}]}

           

          The ejValue.isArray() returns null. it appears to be failing because it's got these additional tags?

          • 2. Re: How to remove additional wrappering from a nested object, for deserialization?
            csa

            Hi Phillip!

             

            It appears you've created your own custom marshallers using @ClientMarshaller and @ServerMarshaller. Is this correct? If so, you do NOT want to activate Jackson marshalling (don't set RestClient.setJacksonMarshallingActive(true)) because then it's assumed that Errai's generated and built-in marshallers will take care of serialization/deserialization and the required ObjectIDs/NumVals etc. are added to the Jackson JSON.

             

            So, setting RestClient.setJacksonMarshallingActive(false) or simply removing this line will cause the unmodified JSON (as emitted by your marshaller) to be used for demarshalling.

             

            Cheers,

            Christian