OLINGO-1292 adding srid serialization and deserialization to server json

This commit is contained in:
shawkins 2018-09-24 10:23:31 -04:00
parent 8cbe468c2a
commit b09dde0b39
5 changed files with 49 additions and 39 deletions

View File

@ -70,6 +70,7 @@ import org.apache.olingo.commons.api.edm.geo.MultiPoint;
import org.apache.olingo.commons.api.edm.geo.MultiPolygon;
import org.apache.olingo.commons.api.edm.geo.Point;
import org.apache.olingo.commons.api.edm.geo.Polygon;
import org.apache.olingo.commons.api.edm.geo.SRID;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.server.api.ServiceMetadata;
import org.apache.olingo.server.api.deserializer.DeserializerException;
@ -769,9 +770,13 @@ public class ODataJsonDeserializer implements ODataDeserializer {
final JsonNode topNode = jsonNode.remove(
geoDataType.equals(GeospatialCollection.class) ? Constants.JSON_GEOMETRIES : Constants.JSON_COORDINATES);
// The "crs" member mentioned in some versions of the OData specification is not part of GeoJSON.
// It used to be used to specify the coordinate reference system.
// TODO: Is it OK to follow RFC 7946 strictly and not allow this element from its obsolete predecessor?
SRID srid = null;
if (jsonNode.has(Constants.JSON_CRS)) {
srid = SRID.valueOf(
jsonNode.remove(Constants.JSON_CRS).get(Constants.PROPERTIES).
get(Constants.JSON_NAME).asText().split(":")[1]);
}
assertJsonNodeIsEmpty(jsonNode);
if (topNode != null && topNode.isArray()) {
@ -779,29 +784,29 @@ public class ODataJsonDeserializer implements ODataDeserializer {
Geospatial.Dimension.GEOMETRY :
Geospatial.Dimension.GEOGRAPHY;
if (geoDataType.equals(Point.class)) {
return readGeoPointValue(name, dimension, topNode);
return readGeoPointValue(name, dimension, topNode, srid);
} else if (geoDataType.equals(MultiPoint.class)) {
return new MultiPoint(dimension, null, readGeoPointValues(name, dimension, 0, false, topNode));
return new MultiPoint(dimension, srid, readGeoPointValues(name, dimension, 0, false, topNode));
} else if (geoDataType.equals(LineString.class)) {
// Although a line string with less than two points is not really one, the OData specification says:
// "The coordinates member of a LineString can have zero or more positions".
// Therefore the required minimal size of the points array currently is zero.
return new LineString(dimension, null, readGeoPointValues(name, dimension, 0, false, topNode));
return new LineString(dimension, srid, readGeoPointValues(name, dimension, 0, false, topNode));
} else if (geoDataType.equals(MultiLineString.class)) {
List<LineString> lines = new ArrayList<LineString>();
for (final JsonNode element : topNode) {
// Line strings can be empty (see above).
lines.add(new LineString(dimension, null, readGeoPointValues(name, dimension, 0, false, element)));
lines.add(new LineString(dimension, srid, readGeoPointValues(name, dimension, 0, false, element)));
}
return new MultiLineString(dimension, null, lines);
return new MultiLineString(dimension, srid, lines);
} else if (geoDataType.equals(Polygon.class)) {
return readGeoPolygon(name, dimension, topNode);
return readGeoPolygon(name, dimension, topNode, srid);
} else if (geoDataType.equals(MultiPolygon.class)) {
List<Polygon> polygons = new ArrayList<Polygon>();
for (final JsonNode element : topNode) {
polygons.add(readGeoPolygon(name, dimension, element));
polygons.add(readGeoPolygon(name, dimension, element, null));
}
return new MultiPolygon(dimension, null, polygons);
return new MultiPolygon(dimension, srid, polygons);
} else if (geoDataType.equals(GeospatialCollection.class)) {
List<Geospatial> elements = new ArrayList<Geospatial>();
for (final JsonNode element : topNode) {
@ -812,7 +817,7 @@ public class ODataJsonDeserializer implements ODataDeserializer {
DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY, name);
}
}
return new GeospatialCollection(dimension, null, elements);
return new GeospatialCollection(dimension, srid, elements);
}
}
}
@ -821,11 +826,11 @@ public class ODataJsonDeserializer implements ODataDeserializer {
DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY, name);
}
private Point readGeoPointValue(final String name, final Geospatial.Dimension dimension, JsonNode node)
private Point readGeoPointValue(final String name, final Geospatial.Dimension dimension, JsonNode node, SRID srid)
throws DeserializerException, EdmPrimitiveTypeException {
if (node.isArray() && (node.size() ==2 || node.size() == 3)
&& node.get(0).isNumber() && node.get(1).isNumber() && (node.get(2) == null || node.get(2).isNumber())) {
Point point = new Point(dimension, null);
Point point = new Point(dimension, srid);
point.setX(getDoubleValue(node.get(0).asText()));
point.setY(getDoubleValue(node.get(1).asText()));
if (node.get(2) != null) {
@ -855,7 +860,7 @@ public class ODataJsonDeserializer implements ODataDeserializer {
if (node.isArray()) {
List<Point> points = new ArrayList<Point>();
for (final JsonNode element : node) {
points.add(readGeoPointValue(name, dimension, element));
points.add(readGeoPointValue(name, dimension, element, null));
}
if (points.size() >= minimalSize
&& (!closed || points.get(points.size() - 1).equals(points.get(0)))) {
@ -866,13 +871,13 @@ public class ODataJsonDeserializer implements ODataDeserializer {
DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY, name);
}
private Polygon readGeoPolygon(final String name, final Geospatial.Dimension dimension, JsonNode node)
private Polygon readGeoPolygon(final String name, final Geospatial.Dimension dimension, JsonNode node, SRID srid)
throws DeserializerException, EdmPrimitiveTypeException {
// GeoJSON would allow for more than one interior polygon (hole).
// But there is no place in the data object to store this information so for now we throw an error.
// There could be a more strict verification that the lines describe boundaries and have the correct winding order.
if (node.isArray() && (node.size() == 1 || node.size() == 2)) {
return new Polygon(dimension, null,
return new Polygon(dimension, srid,
node.size() > 1 ? readGeoPointValues(name, dimension, 4, true, node.get(1)) : null,
readGeoPointValues(name, dimension, 4, true, node.get(0)));
}

View File

@ -63,6 +63,7 @@ import org.apache.olingo.commons.api.edm.geo.MultiPoint;
import org.apache.olingo.commons.api.edm.geo.MultiPolygon;
import org.apache.olingo.commons.api.edm.geo.Point;
import org.apache.olingo.commons.api.edm.geo.Polygon;
import org.apache.olingo.commons.api.edm.geo.SRID;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
import org.apache.olingo.server.api.ODataServerError;
@ -920,7 +921,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
writePrimitiveValue(property.getName(), type, property.asPrimitive(),
isNullable, maxLength, precision, scale, isUnicode, json);
} else if (property.isGeospatial()) {
writeGeoValue(property.getName(), type, property.asGeospatial(), isNullable, json);
writeGeoValue(property.getName(), type, property.asGeospatial(), isNullable, json, null);
} else if (property.isEnum()) {
writePrimitiveValue(property.getName(), type, property.asEnum(),
isNullable, maxLength, precision, scale, isUnicode, json);
@ -976,7 +977,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
/** Writes a geospatial value following the GeoJSON specification defined in RFC 7946. */
protected void writeGeoValue(final String name, final EdmPrimitiveType type, final Geospatial geoValue,
final Boolean isNullable, JsonGenerator json)
final Boolean isNullable, JsonGenerator json, SRID parentSrid)
throws EdmPrimitiveTypeException, IOException, SerializerException {
if (geoValue == null) {
if (isNullable == null || isNullable) {
@ -988,10 +989,6 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
if (!type.getDefaultType().isAssignableFrom(geoValue.getClass())) {
throw new EdmPrimitiveTypeException("The value type " + geoValue.getClass() + " is not supported.");
}
if (geoValue.getSrid() != null && geoValue.getSrid().isNotDefault()) {
throw new SerializerException("Non-standard SRID not supported!",
SerializerException.MessageKeys.WRONG_PROPERTY_VALUE, name, geoValue.toString());
}
json.writeStartObject();
json.writeStringField(Constants.ATTR_TYPE, geoValueTypeToJsonName.get(geoValue.getGeoType()));
json.writeFieldName(geoValue.getGeoType() == Geospatial.Type.GEOSPATIALCOLLECTION ?
@ -1028,15 +1025,29 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
case GEOSPATIALCOLLECTION:
for (final Geospatial element : (GeospatialCollection) geoValue) {
writeGeoValue(name, EdmPrimitiveTypeFactory.getInstance(element.getEdmPrimitiveTypeKind()),
element, isNullable, json);
element, isNullable, json, geoValue.getSrid());
}
break;
}
json.writeEndArray();
if (geoValue.getSrid() != null && geoValue.getSrid().isNotDefault()
&& (parentSrid == null || !parentSrid.equals(geoValue.getSrid()))) {
srid(json, geoValue.getSrid());
}
json.writeEndObject();
}
}
private void srid(final JsonGenerator jgen, final SRID srid) throws IOException {
jgen.writeObjectFieldStart(Constants.JSON_CRS);
jgen.writeStringField(Constants.ATTR_TYPE, Constants.JSON_NAME);
jgen.writeObjectFieldStart(Constants.PROPERTIES);
jgen.writeStringField(Constants.JSON_NAME, "EPSG:" + srid.toString());
jgen.writeEndObject();
jgen.writeEndObject();
}
private void writeGeoPoint(JsonGenerator json, final Point point) throws IOException {
json.writeNumber(point.getX());
json.writeNumber(point.getY());

View File

@ -896,12 +896,14 @@ public class ODataJsonDeserializerEntityTest extends AbstractODataDeserializerTe
public void geoPoint() throws Exception {
final EdmEntityType entityType = mockEntityType(EdmPrimitiveTypeKind.GeometryPoint);
final String preamble = "{\"" + entityType.getPropertyNames().get(0) + "\":{";
final Entity entity = deserialize(preamble + "\"type\":\"Point\",\"coordinates\":[1.25,2.75]}}",
final Entity entity = deserialize(preamble + "\"type\":\"Point\",\"coordinates\":[1.25,2.75]," +
"\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:42\"}}}}",
entityType);
assertEquals(1, entity.getProperties().size());
assertTrue(entity.getProperties().get(0).getValue() instanceof Point);
final Point point = (Point) entity.getProperties().get(0).getValue();
assertEquals(Geospatial.Dimension.GEOMETRY, point.getDimension());
assertEquals("42", point.getSrid().toString());
assertEquals(1.25, point.getX(), 0);
assertEquals(2.75, point.getY(), 0);

View File

@ -18,9 +18,7 @@
*/
package org.apache.olingo.server.core.serializer.json;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.io.InputStream;
@ -2306,12 +2304,9 @@ public class ODataJsonSerializerTest {
final Entity entity = new Entity()
.addProperty(new Property(null, entityType.getPropertyNames().get(0), ValueType.GEOSPATIAL,
new Point(Dimension.GEOMETRY, SRID.valueOf("42"))));
try {
serializerNoMetadata.entity(metadata, entityType, entity, null);
fail("Expected exception not thrown.");
} catch (final SerializerException e) {
assertNotNull(e);
}
Assert.assertEquals("{\"PropertyGeometryPoint\":{\"type\":\"Point\",\"coordinates\":[0.0,0.0],"
+ "\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:42\"}}}}",
IOUtils.toString(serializerNoMetadata.entity(metadata, entityType, entity, null).getContent()));
}
private Point createPoint(final double x, final double y) {

View File

@ -2312,12 +2312,9 @@ public class ODataJsonSerializerv01Test {
final Entity entity = new Entity()
.addProperty(new Property(null, entityType.getPropertyNames().get(0), ValueType.GEOSPATIAL,
new Point(Dimension.GEOMETRY, SRID.valueOf("42"))));
try {
serializerNoMetadata.entity(metadata, entityType, entity, null);
fail("Expected exception not thrown.");
} catch (final SerializerException e) {
assertNotNull(e);
}
Assert.assertEquals("{\"PropertyGeometryPoint\":{\"type\":\"Point\",\"coordinates\":[0.0,0.0],"
+ "\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:42\"}}}}",
IOUtils.toString(serializerNoMetadata.entity(metadata, entityType, entity, null).getContent()));
}
private Point createPoint(final double x, final double y) {