Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for heterogeneous schemas #173

Closed
wonderfly opened this issue Jan 9, 2015 · 11 comments
Closed

Support for heterogeneous schemas #173

wonderfly opened this issue Jan 9, 2015 · 11 comments
Assignees
Labels
type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.

Comments

@wonderfly
Copy link
Contributor

From yan...@google.com on December 13, 2012 06:11:06

External references, such as a standards document, or specification? http://www.geojson.org/geojson-spec.html Java environments (e.g. Java 6, Android 2.3, App Engine, or All)? All Please describe the feature requested. How can we elegantly support GeoJSON? Example from the spec:

{ "type": "FeatureCollection",
"features": [
{ "type": "Feature",
"geometry": {"type": "Point", "coordinates": [102.0, 0.5]},
"properties": {"prop0": "value0"}
},
{ "type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [
[102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]
]
},
"properties": {
"prop0": "value0",
"prop1": 0.0
}
},
{ "type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],
[100.0, 1.0], [100.0, 0.0] ]
]
},
"properties": {
"prop0": "value0",
"prop1": {"this": "that"}
}
}
]
}

Note that "type" is case sensitive and can be any of "Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon", "GeometryCollection", "Feature", or "FeatureCollection". Each type corresponds to a different schema. For example for Geometry "coordinates" JSON key has a different type for different geometries. For example, for Point it may be represented as List, for LineString it may be represented as List<List>, and for Polygon it may be represented as List<List<List>>.

What you'd have to do today is to merge the all into something like:

class GeoJsonObject extends GenericJson {
@key String type;
@key List coordinates;
@key Geometry geometry;
@key Map<String, Object> properties;
}

Note for example how coordinates has to be List and in usage it would have to be cast to the appropriate type.

But really we'd ideally like something like

class GeoJsonObject extends GenericJson {
@key String type;
}

class Feature extends GeoJsonObject {
@key Geometry geometry;
@key Map<String, Object> properties;
}

class Geometry extends GeoJsonObject {
@key Object coordinates;
}

public class Point extends Geometry {
@key List coordinates;
}

public class Point extends Geometry {
@key List coordinates;
}

public class LineString extends Geometry {
@key List<List> coordinates;
}

public class Polygon extends Geometry {
@key List<List<List>> coordinates;
}

But the question is how do we cause it to be parsed into the appropriate Java Type based on the "type" JSON key.

One option is we could enable some interface like:

class GeoJsonObject extends GenericJson implements JsonMultiType {
...
Type chooseType(ArrayMap map) {
switch(map.get("type")) {
case "Feature": return Feature.class;
case "Point": return Point.class;
case "LineString": return LineString.class;
case "Polygon": return Polygon.class;
...
}
}

and then internally we would parse it into an ArrayMap first, and then reparse it into the given type afterwards.

Another option is to generalize it a bit more using a post-process step, e.g.:

class GeoJsonObject extends GenericJson implements PostProcess {
...
GeoJsonObject postProcess(ArrayMap map) {
switch(map.get("type")) {
case "Feature": return parseAs(map, Feature.class);
case "Point": return parseAs(map, Point.class);
case "LineString": return parseAs(map, LineString.class);
case "Polygon": return parseAs(map, Polygon.class);
...
}
}

and we'd have to provide this new "parseAs" method.

Of course I'm open to other ideas as well, so feel free to suggest other approaches.

Original issue: http://code.google.com/p/google-http-java-client/issues/detail?id=173

@wonderfly wonderfly added type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design. imported labels Jan 9, 2015
@wonderfly wonderfly self-assigned this Jan 9, 2015
@wonderfly
Copy link
Contributor Author

From yan...@google.com on January 09, 2013 16:30:28

Labels: -Priority-Medium -Milestone-Version1.15.0 Priority-High Milestone-Version1.14.0

@wonderfly
Copy link
Contributor Author

From jmcg...@google.com on January 09, 2013 20:47:53

Some initial thoughts:

Should LineString be @key List<List> coordinates or @key List coordinates?

How would you initialize a Polygon? Does Java have a simple initializer for Lists of Lists of Lists?

@wonderfly
Copy link
Contributor Author

From yan...@google.com on January 12, 2013 07:12:11

James, I think those are good questions. But I hadn't intended for that to be the purpose of this feature request. So if you don't mind let me narrow the focus of this feature request to support for heterogeneous schemas, meaning schemas for whom there is a choice of which schema to use based on the value of a single top-level property. GeoJson is a good example of that, but in our implementation we will suppose that other use cases exist, so we will implement it to work for the most general use cases.

I think it may be worthwhile to look at other JSON <-> Java mapping libraries like Jackson or GSON and see how they implement this.

Summary: Support for heterogeneous schemas (was: Elegant support for GeoJSON)

@wonderfly
Copy link
Contributor Author

From yan...@google.com on January 17, 2013 19:09:30

Labels: -Milestone-Version1.14.0 Milestone-Version1.15.0

@wonderfly
Copy link
Contributor Author

From yan...@google.com on January 18, 2013 18:25:31

For reference, GSON allows you to register a JSON deserializer or a type adapter: http://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/JsonDeserializer.html http://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/TypeAdapter.html Jackson has an even more sophisticated mechanism with even a built-in concept of registering different types based on the value of a property: http://programmerbruce.blogspot.com/2011/05/deserialize-json-with-jackson-into.html Sample code snippet copied from the above blog:

@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
@JsonSubTypes({
@type(value = Cat.class, name = "cat"),
@type(value = Dog.class, name = "dog") })
abstract class Animal
{
public String name;
}

class Dog extends Animal
{
public String breed;
public String leashColor;
}

class Cat extends Animal
{
public String favoriteToy;
}

I like this annotation based approach, and that seems to me the most appealing approach for use case at hand. We may have to provide greater customization in the future, but for now this seems like the simplest solution.

@wonderfly
Copy link
Contributor Author

From yan...@google.com on January 24, 2013 05:49:50

Labels: -Milestone-Version1.15.0 Milestone-Version1.14.0

@wonderfly
Copy link
Contributor Author

From yan...@google.com on February 04, 2013 20:48:22

Labels: -Milestone-Version1.14.0 Milestone-Version1.15.0

@wonderfly
Copy link
Contributor Author

From yincrash on March 04, 2013 11:28:26

Something I would like to see handled is the unknown case. Supporting a 'default' case if the JSON type is not known to the client.

@wonderfly
Copy link
Contributor Author

From yan...@google.com on March 25, 2013 12:32:37

Owner: ngmic...@google.com
Cc: -ngmic...@google.com yan...@google.com
Labels: -Milestone-Version1.15.0 Milestone-Version1.16.0

@wonderfly
Copy link
Contributor Author

From ngmic...@google.com on May 28, 2013 08:43:10

https://codereview.appspot.com/9618044/

Status: Started

@wonderfly
Copy link
Contributor Author

From ngmic...@google.com on June 27, 2013 17:10:26

Hi Michael,
A "default" case was not part of the initial set of requirements for the feature, but it certainly could be useful. If this is something you would like to see and have a use-case for, please feel free to open a new feature request, and include as much detail as possible.
One way you could use the features implemented is to make your type field a value type, such as an int. Currently, if the type field is null, an exception is thrown. However, if the type field is an unspecified int, it will have a value of 0. You could then define 0 to map to whatever default type you want. Of course, this is not a perfect design, as it wouldn't work if a value of "0" actually had meaning to your data.

Status: Fixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.
Projects
None yet
Development

No branches or pull requests

1 participant