-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from RolandKoenig/feature/2-async-overloads-for…
…-loading-and-saving Async overloads for loading and saving
- Loading branch information
Showing
5 changed files
with
356 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,61 @@ | ||
# RolandK.Formats.Gpx <img src="assets/Logo_128.png" width="32" /> | ||
### Common Information | ||
## Common Information | ||
A .NET Standard library for reading and writing GPX (GPS Exchange Format) files. | ||
This library was build for my [GpxViewer](https://github.com/RolandKoenig/GpxViewer) project. It is based | ||
on the System.Xml.Serialization.XmlSerializer class and therefore available for .NET Framework and .NET Core projects. | ||
|
||
### Build | ||
## Build | ||
[![Continuous integration](https://github.com/RolandKoenig/RolandK.Formats.Gpx/actions/workflows/continuous-integration.yml/badge.svg)](https://github.com/RolandKoenig/RolandK.Formats.Gpx/actions/workflows/continuous-integration.yml) | ||
|
||
### Feature overview | ||
## Feature overview | ||
- Full document model for gpx files | ||
- Load gpx files | ||
- Write gpx files | ||
- Add custom metadata to gpx files | ||
- Don't lose other custom metadate after loading and saving gpx files | ||
- Add custom xml extensions to gpx files | ||
- Don't lose other custom xml extensions information after loading and saving gpx files | ||
|
||
## Samples | ||
### Load GPX file | ||
Load file file (here Kösseine.gpx) and get total count of tracks, routes and waypoints in the file | ||
```csharp | ||
var gpxFile = await GpxFile.LoadAsync("Kösseine.gpx"); | ||
|
||
var countTracks = gpxFile.Tracks.Count; | ||
var countRoutes = gpxFile.Routes.Count; | ||
var countWaypoints = gpxFile.Waypoints.Count; | ||
``` | ||
|
||
### Save GPX file | ||
Save a previously loaded / created / modified file | ||
```csharp | ||
await GpxFile.SaveAsync(gpxFile, "MyFile.gpx"); | ||
``` | ||
|
||
### Add custom xml extension | ||
Your have to define your own xml extension types and give them a namespace | ||
```csharp | ||
[XmlType("MyTrackExtension", Namespace = "http://testing.rolandk.net/")] | ||
public class MyTrackExtension | ||
{ | ||
public bool AlreadyDone { get; set; } = false; | ||
} | ||
``` | ||
|
||
Then you have to register these xml extension types and their namespaces to GpxFile. | ||
You should do this somewhere in your startup code of your application. | ||
```csharp | ||
// You have to register your own xml extension types | ||
GpxFile.RegisterExtensionType(typeof(MyTrackExtension)); | ||
GpxFile.RegisterNamespace("rktest", "http://testing.rolandk.net/"); | ||
``` | ||
|
||
Now you can access these xml extensions from c# code | ||
```csharp | ||
var gpxFile = await GpxFile.LoadAsync(inStream); | ||
var gpxTrack = gpxFile.Tracks[0]; | ||
|
||
gpxTrack.Extensions ??= new GpxExtensions(); | ||
var myTrackExtension = gpxTrack.Extensions.GetOrCreateExtension<MyTrackExtension>(); | ||
|
||
myTrackExtension.AlreadyDone = true; // This property comes from our own xml extension | ||
``` |
48 changes: 48 additions & 0 deletions
48
src/RolandK.Formats.Gpx.Tests/FileLoad/GpxFileCustomExtensionsTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
using System.Xml.Serialization; | ||
|
||
namespace RolandK.Formats.Gpx.Tests.FileLoad; | ||
|
||
public class GpxFileCustomExtensionsTests | ||
{ | ||
[Fact] | ||
public async Task AddCustomMetadata() | ||
{ | ||
// Arrange | ||
await using var inStream = GpxTestUtilities.ReadFromEmbeddedResource( | ||
typeof(GpxFileLoadTests),"Test_Gpx1_1.gpx"); | ||
|
||
// Act | ||
GpxFile.RegisterExtensionType(typeof(MyTrackExtension)); | ||
GpxFile.RegisterNamespace("rktest", "http://testing.rolandk.net/"); | ||
|
||
var originalGpxFile = await GpxFile.LoadAsync(inStream); | ||
var originalGpxTrack = originalGpxFile.Tracks[0]; | ||
|
||
originalGpxTrack.Extensions ??= new GpxExtensions(); | ||
var myTrackExtension = originalGpxTrack.Extensions.GetOrCreateExtension<MyTrackExtension>(); | ||
|
||
myTrackExtension.AlreadyDone = true; | ||
|
||
using var writingMemoryStream = new MemoryStream(1024 * 100); | ||
await GpxFile.SaveAsync(originalGpxFile, writingMemoryStream); | ||
|
||
using var readingMemoryStream = new MemoryStream(writingMemoryStream.GetBuffer()); | ||
var reloadedFile = await GpxFile.LoadAsync(readingMemoryStream); | ||
|
||
// Assert | ||
Assert.Single(reloadedFile.Tracks); | ||
|
||
var reloadedTrack = reloadedFile.Tracks[0]; | ||
Assert.NotNull(reloadedTrack.Extensions); | ||
|
||
var reloadedExtension = reloadedTrack.Extensions.TryGetSingleExtension<MyTrackExtension>(); | ||
Assert.NotNull(reloadedExtension); | ||
Assert.True(reloadedExtension.AlreadyDone); | ||
} | ||
|
||
[XmlType("MyTrackExtension", Namespace = "http://testing.rolandk.net/")] | ||
public class MyTrackExtension | ||
{ | ||
public bool AlreadyDone { get; set; } | ||
} | ||
} |
107 changes: 107 additions & 0 deletions
107
src/RolandK.Formats.Gpx.Tests/FileLoad/GpxFileLoadAsyncTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Text; | ||
|
||
namespace RolandK.Formats.Gpx.Tests.FileLoad; | ||
|
||
[SuppressMessage("ReSharper", "RedundantArgumentDefaultValue")] | ||
public class GpxFileLoadAsyncTests | ||
{ | ||
[Fact] | ||
public async Task GpxVersion1_1_CompatibilityMode() | ||
{ | ||
// Arrange | ||
await using var inStream = GpxTestUtilities.ReadFromEmbeddedResource( | ||
typeof(GpxFileLoadTests),"Test_Gpx1_1.gpx"); | ||
|
||
// Act | ||
var gpxFile = await GpxFile.LoadAsync(inStream, GpxFileDeserializationMethod.Compatibility); | ||
|
||
// Assert | ||
Assert.NotNull(gpxFile); | ||
Assert.NotNull(gpxFile.Metadata); | ||
Assert.Equal("Kösseine", gpxFile.Metadata.Name); | ||
Assert.Single(gpxFile.Tracks); | ||
Assert.Single(gpxFile.Tracks[0].Segments); | ||
Assert.Equal(228, gpxFile.Tracks[0].Segments[0].Points.Count); | ||
} | ||
|
||
[Fact] | ||
public async Task GpxVersion1_1_Gpx1_1Mode() | ||
{ | ||
// Arrange | ||
await using var inStream = GpxTestUtilities.ReadFromEmbeddedResource( | ||
typeof(GpxFileLoadTests),"Test_Gpx1_1.gpx"); | ||
|
||
// Act | ||
var gpxFile = await GpxFile.LoadAsync(inStream, GpxFileDeserializationMethod.OnlyGpx1_1); | ||
|
||
// Assert | ||
Assert.NotNull(gpxFile); | ||
Assert.NotNull(gpxFile.Metadata); | ||
Assert.Equal("Kösseine", gpxFile.Metadata.Name); | ||
Assert.Single(gpxFile.Tracks); | ||
Assert.Single(gpxFile.Tracks[0].Segments); | ||
Assert.Equal(228, gpxFile.Tracks[0].Segments[0].Points.Count); | ||
} | ||
|
||
[Fact] | ||
public async Task GpxVersion1_1_on_xml_1_1() | ||
{ | ||
// Arrange | ||
await using var inStream = GpxTestUtilities.ReadFromEmbeddedResource( | ||
typeof(GpxFileLoadTests),"Test_Gpx1_1_on_xml_1_1.gpx"); | ||
|
||
// Act | ||
var gpxFile = await GpxFile.LoadAsync(inStream, GpxFileDeserializationMethod.Compatibility); | ||
|
||
// Assert | ||
Assert.NotNull(gpxFile); | ||
Assert.NotNull(gpxFile.Metadata); | ||
Assert.Equal("Kösseine", gpxFile.Metadata.Name); | ||
Assert.Single(gpxFile.Tracks); | ||
Assert.Single(gpxFile.Tracks[0].Segments); | ||
Assert.Equal(228, gpxFile.Tracks[0].Segments[0].Points.Count); | ||
} | ||
|
||
[Fact] | ||
public async Task GpxVersion1_0() | ||
{ | ||
// Arrange | ||
await using var inStream = GpxTestUtilities.ReadFromEmbeddedResource( | ||
typeof(GpxFileLoadTests),"Test_Gpx1_0.gpx"); | ||
|
||
// Act | ||
var gpxFile = await GpxFile.LoadAsync(inStream, GpxFileDeserializationMethod.Compatibility); | ||
|
||
// Assert | ||
Assert.NotNull(gpxFile); | ||
Assert.NotNull(gpxFile.Metadata); | ||
Assert.Equal("Kösseine", gpxFile.Metadata.Name); | ||
Assert.Single(gpxFile.Tracks); | ||
Assert.Single(gpxFile.Tracks[0].Segments); | ||
Assert.Equal(228, gpxFile.Tracks[0].Segments[0].Points.Count); | ||
} | ||
|
||
[Fact] | ||
public async Task GpxVersion1_0_SaveAs1_1() | ||
{ | ||
// Arrange | ||
await using var inStream = GpxTestUtilities.ReadFromEmbeddedResource( | ||
typeof(GpxFileLoadTests),"Test_Gpx1_0.gpx"); | ||
|
||
// Act | ||
var gpxFile = await GpxFile.LoadAsync(inStream, GpxFileDeserializationMethod.Compatibility); | ||
var outStrBuilder = new StringBuilder(33000); | ||
using (var strWriter = new StringWriter(outStrBuilder)) | ||
{ | ||
await GpxFile.SaveAsync(gpxFile, strWriter); | ||
} | ||
var writtenFile = outStrBuilder.ToString(); | ||
|
||
// Assert | ||
Assert.True(writtenFile.Contains("version=\"1.1\""), "Version attribute"); | ||
Assert.True(writtenFile.Contains("xmlns=\"http://www.topografix.com/GPX/1/1\""), "Default namespace"); | ||
|
||
Assert.Equal("1.0", gpxFile.Version); | ||
} | ||
} |
49 changes: 36 additions & 13 deletions
49
src/RolandK.Formats.Gpx.Tests/FileLoad/GpxFileLoadTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,84 +1,107 @@ | ||
using System.Text; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Text; | ||
|
||
namespace RolandK.Formats.Gpx.Tests.FileLoad; | ||
|
||
[SuppressMessage("ReSharper", "RedundantArgumentDefaultValue")] | ||
public class GpxFileLoadTests | ||
{ | ||
[Fact] | ||
public void GpxVersion1_1_CompatibilityMode() | ||
{ | ||
// Arrange | ||
using var inStream = GpxTestUtilities.ReadFromEmbeddedResource( | ||
typeof(GpxFileLoadTests),"Test_Gpx1_1.gpx"); | ||
|
||
var gpxFile = GpxFile.Deserialize(inStream, GpxFileDeserializationMethod.Compatibility); | ||
// Act | ||
var gpxFile = GpxFile.Load(inStream, GpxFileDeserializationMethod.Compatibility); | ||
|
||
// Assert | ||
Assert.NotNull(gpxFile); | ||
Assert.NotNull(gpxFile.Metadata); | ||
Assert.Equal("Kösseine", gpxFile!.Metadata!.Name); | ||
Assert.Equal("Kösseine", gpxFile.Metadata.Name); | ||
Assert.Single(gpxFile.Tracks); | ||
Assert.Single(gpxFile.Tracks[0].Segments); | ||
Assert.Equal(228, gpxFile.Tracks[0].Segments[0].Points.Count); | ||
} | ||
|
||
[Fact] | ||
public void GpxVersion1_1_Gpx1_1Mode() | ||
{ | ||
// Arrange | ||
using var inStream = GpxTestUtilities.ReadFromEmbeddedResource( | ||
typeof(GpxFileLoadTests),"Test_Gpx1_1.gpx"); | ||
|
||
var gpxFile = GpxFile.Deserialize(inStream, GpxFileDeserializationMethod.OnlyGpx1_1); | ||
// Act | ||
var gpxFile = GpxFile.Load(inStream, GpxFileDeserializationMethod.OnlyGpx1_1); | ||
|
||
// Assert | ||
Assert.NotNull(gpxFile); | ||
Assert.NotNull(gpxFile.Metadata); | ||
Assert.Equal("Kösseine", gpxFile!.Metadata!.Name); | ||
Assert.Equal("Kösseine", gpxFile.Metadata.Name); | ||
Assert.Single(gpxFile.Tracks); | ||
Assert.Single(gpxFile.Tracks[0].Segments); | ||
Assert.Equal(228, gpxFile.Tracks[0].Segments[0].Points.Count); | ||
} | ||
|
||
[Fact] | ||
public void GpxVersion1_1_on_xml_1_1() | ||
{ | ||
// Arrange | ||
using var inStream = GpxTestUtilities.ReadFromEmbeddedResource( | ||
typeof(GpxFileLoadTests),"Test_Gpx1_1_on_xml_1_1.gpx"); | ||
|
||
var gpxFile = GpxFile.Deserialize(inStream, GpxFileDeserializationMethod.Compatibility); | ||
// Act | ||
var gpxFile = GpxFile.Load(inStream, GpxFileDeserializationMethod.Compatibility); | ||
|
||
// Assert | ||
Assert.NotNull(gpxFile); | ||
Assert.NotNull(gpxFile.Metadata); | ||
Assert.Equal("Kösseine", gpxFile!.Metadata!.Name); | ||
Assert.Equal("Kösseine", gpxFile.Metadata.Name); | ||
Assert.Single(gpxFile.Tracks); | ||
Assert.Single(gpxFile.Tracks[0].Segments); | ||
Assert.Equal(228, gpxFile.Tracks[0].Segments[0].Points.Count); | ||
} | ||
|
||
[Fact] | ||
public void GpxVersion1_0() | ||
{ | ||
// Arrange | ||
using var inStream = GpxTestUtilities.ReadFromEmbeddedResource( | ||
typeof(GpxFileLoadTests),"Test_Gpx1_0.gpx"); | ||
|
||
var gpxFile = GpxFile.Deserialize(inStream, GpxFileDeserializationMethod.Compatibility); | ||
// Act | ||
var gpxFile = GpxFile.Load(inStream, GpxFileDeserializationMethod.Compatibility); | ||
|
||
// Assert | ||
Assert.NotNull(gpxFile); | ||
Assert.NotNull(gpxFile.Metadata); | ||
Assert.Equal("Kösseine", gpxFile!.Metadata!.Name); | ||
Assert.Equal("Kösseine", gpxFile.Metadata.Name); | ||
Assert.Single(gpxFile.Tracks); | ||
Assert.Single(gpxFile.Tracks[0].Segments); | ||
Assert.Equal(228, gpxFile.Tracks[0].Segments[0].Points.Count); | ||
} | ||
|
||
[Fact] | ||
public void GpxVersion1_0_SaveAs1_1() | ||
{ | ||
// Arrange | ||
using var inStream = GpxTestUtilities.ReadFromEmbeddedResource( | ||
typeof(GpxFileLoadTests),"Test_Gpx1_0.gpx"); | ||
|
||
var gpxFile = GpxFile.Deserialize(inStream, GpxFileDeserializationMethod.Compatibility); | ||
// Act | ||
var gpxFile = GpxFile.Load(inStream, GpxFileDeserializationMethod.Compatibility); | ||
var outStrBuilder = new StringBuilder(33000); | ||
using (var strWriter = new StringWriter(outStrBuilder)) | ||
{ | ||
GpxFile.Serialize(gpxFile, strWriter); | ||
GpxFile.Save(gpxFile, strWriter); | ||
} | ||
var writtenFile = outStrBuilder.ToString(); | ||
|
||
// Check output | ||
// Assert | ||
Assert.True(writtenFile.Contains("version=\"1.1\""), "Version attribute"); | ||
Assert.True(writtenFile.Contains("xmlns=\"http://www.topografix.com/GPX/1/1\""), "Default namespace"); | ||
|
||
// Check original data | ||
Assert.Equal("1.0", gpxFile.Version); | ||
} | ||
} |
Oops, something went wrong.