Hi,
Just wondering if anyone has already tried returning GeoJson type with NSwag?
I've tried it in combination with GeoJSON.Net, but than NSwag renders the following typescript:
export abstract class IPosition implements IIPosition {
constructor(data?: IIPosition) {
if (data) {
for (var property in data) {
if (data.hasOwnProperty(property))
(<any>this)[property] = (<any>data)[property];
}
}
}
init(data?: any) {
if (data) {
}
}
static fromJS(data: any): IPosition {
data = typeof data === 'object' ? data : {};
throw new Error("The abstract class 'IPosition' cannot be instantiated.");
}
toJSON(data?: any) {
data = typeof data === 'object' ? data : {};
return data;
}
}
The Problem is it sees IPosition as an abstract class and thus it can be deserialized. But it is needed as input for the following LineString:
export class LineString extends GeoJSONObject implements ILineString {
type!: GeoJSONObjectType;
coordinates!: IPosition[];
constructor(data?: ILineString) {
super(data);
if (!data) {
this.coordinates = [];
}
}
init(data?: any) {
super.init(data);
if (data) {
this.type = data["type"];
if (data["coordinates"] && data["coordinates"].constructor === Array) {
this.coordinates = [];
for (let item of data["coordinates"])
this.coordinates.push(IPosition.fromJS(item));
}
}
}
static fromJS(data: any): LineString {
data = typeof data === 'object' ? data : {};
let result = new LineString();
result.init(data);
return result;
}
toJSON(data?: any) {
data = typeof data === 'object' ? data : {};
data["type"] = this.type;
if (this.coordinates && this.coordinates.constructor === Array) {
data["coordinates"] = [];
for (let item of this.coordinates)
data["coordinates"].push(item.toJSON());
}
super.toJSON(data);
return data;
}
}
Maybe I'm using the wrong .Net Library or is there a way to not generate the types and maybe use TypeScript equivalents from an external library?
Tips are welcome.
Regards,
Jochen
Follow up.
I think it is not a good idea to let NSwag generate the GeoJSON.Net types to typescript. I got is somehow working with some manual adjustments.
First of all I excluded the GoeJSON.Net type names in the nswag configuration.
"excludedTypeNames": [
"GeoJSONObject",
"GeoJSONObjectType",
"IPosition",
"ICRSObject",
"LineString"
],
Then in the generated typescript code I added an extra import: import { LineString } from 'geojson';. To be able to use that you have to install @types/geojson from npm.
The last issue to solve is in the generated ctor, init and toJSON functions. In comment you can see the lines that were generated and that I changed:
export class SuggestionFilters extends OrderFilters implements ISuggestionFilters {
/** Ordered list of coordinates that will make up the line of the polygon */
coordinates!: LineString;
/** Distance in meters from the line the orders should be searched. Defaults to 20km. */
buffer!: number;
constructor(data?: ISuggestionFilters) {
super(data);
if (!data) {
// this.coordinates = new LineString();
}
}
init(data?: any) {
super.init(data);
if (data) {
// this.coordinates = data["coordinates"] ? LineString.fromJS(data["coordinates"]) : new LineString();
this.coordinates = data["coordinates"];
this.buffer = data["buffer"];
}
}
static fromJS(data: any): SuggestionFilters {
data = typeof data === 'object' ? data : {};
let result = new SuggestionFilters();
result.init(data);
return result;
}
toJSON(data?: any) {
data = typeof data === 'object' ? data : {};
// data["coordinates"] = this.coordinates ? this.coordinates.toJSON() : <any>undefined;
data["coordinates"] = this.coordinates;
data["buffer"] = this.buffer;
super.toJSON(data);
return data;
}
}
Is there a way to let NSwag know it has to handle some types just as the primitive types? This would solve my manual editing.
Regards,
Jochen
Is there a way to let NSwag know it has to handle some types just as the primitive types?
Can you describe this feature a little bit more in detail? Some sample input/schema/output?
From some types in .Net I use in my public API I do not want NSwag to generate the javascript/typescript counterpart because they are already part of my code or an external library.
In my case I have GeoJSON.Net on the API (.Net) side and @types/geojson on the client/ typescript side.
I was able to fix it partially with excludedTypeNames, NSwag then doesn't generate the classes. But it still expects that the class has a .toSJON() and .fromJS() method. Where I expected them to be handled as a string or int.
// data["coordinates"] = this.coordinates ? this.coordinates.toJSON() : <any>undefined;
data["coordinates"] = this.coordinates;
The first line is what has been generated and the second is what I expected.
I solved my issue by extending my GeoJson objects with a toJSON and fromJS method.
My question is to be able to tell NSwag that I'm providing the code myself for certain types and that they don't need an fromJS an toJSON method.
Having the same/similar issue with GeoJSON but with the Microsoft.Azure.Documents.Spatial.Point object
The server will send
"location":{"type":"Point","coordinates":[-0.332437,51.374954]}}}
But the generated class wont load the coordinates array
"location": {
"type": "Point",
"coordinates": {
"longitude": null,
"latitude": null,
"altitude": null
},
There is a custom JSON converter in the .net code which may be causing issues, but I need to find a way to get the generated code to init this data correctly.
Thanks
decompiled dot net Point type below for reference
namespace Microsoft.Azure.Documents.Spatial
{
public sealed class Point : Geometry
{
[JsonProperty("coordinates")]
public Position Position
{
[CompilerGenerated]
get;
[CompilerGenerated]
private set;
}
public Point(double longitude, double latitude)
: this(new Position(longitude, latitude), new GeometryParams())
{
}
public Point(Position position)
: this(position, new GeometryParams())
{
}
namespace Microsoft.Azure.Documents.Spatial
{
[JsonConverter(typeof(PositionJsonConverter))]
public sealed class Position
{
public ReadOnlyCollection<double> Coordinates
{
[CompilerGenerated]
get;
[CompilerGenerated]
private set;
}
public double Longitude => Coordinates.get_Item(0);
public double Latitude => Coordinates.get_Item(1);
public double? Altitude
{
get
{
if (Coordinates.get_Count() <= 2)
{
return null;
}
return Coordinates.get_Item(2);
}
}
Maybe implement a custom type mapper for this type?
I solved my issue by extending my GeoJson objects with a toJSON and fromJS method.
How did you extend the interfaces in @types/geojson?
@Silrem here's an example of Point
import {
Point as _point,
} from '@turf/turf';
export class Point implements _point {
type: 'Point';
coordinates: number[];
bbox?;
constructor(data?: _point) {
if (data) {
for (const property in data) {
if (data.hasOwnProperty(property)) {
(<any>this)[property] = (<any>data)[property];
}
}
}
}
static fromJS(data: any): Point {
data = typeof data === 'object' ? data : {};
const result = new Point();
result.init(data);
return result;
}
init(data?: any) {
if (data) {
this.type = data['type'];
this.coordinates = data['coordinates'];
this.bbox = data['bbox'];
}
}
toJSON(data?: any): any {
data = typeof data === 'object' ? data : {};
data['type'] = this.type;
data['coordinates'] = this.coordinates;
data['bbox'] = this.bbox;
return data;
}
}