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

Make iiif-net parse all the Manifests in the Cookbook #51

Merged
merged 4 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions src/IIIF/IIIF.Tests/Serialisation/CookbookDeserialization.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using IIIF.Presentation.V3;
using IIIF.Serialisation;

namespace IIIF.Tests.Serialisation;

public class CookbookDeserialization
{
private Collection theseusCollection;
private HttpClient httpClient;
private List<string> skip;

public CookbookDeserialization()
{
httpClient = new HttpClient();
var s = httpClient.GetStringAsync("https://theseus-viewer.netlify.app/cookbook-collection.json").Result;
donaldgray marked this conversation as resolved.
Show resolved Hide resolved
theseusCollection = s.FromJson<Collection>();

// these have bugs in the cookbook, see https://github.com/IIIF/cookbook-recipes/pull/546
skip = new List<string>
{
"https://iiif.io/api/cookbook/recipe/0219-using-caption-file/manifest.json",
"https://iiif.io/api/cookbook/recipe/0040-image-rotation-service/manifest-service.json"
};
}

[Fact]
public void Can_Deserialize_Cookbook_Collection()
{
foreach (var item in theseusCollection.Items!)
donaldgray marked this conversation as resolved.
Show resolved Hide resolved
{
if (item is Manifest manifestRef)
{
if (!skip.Contains(manifestRef.Id))
{
var s = httpClient.GetStringAsync(manifestRef.Id).Result;
var manifest = s.FromJson<Manifest>();
// perfunctory assertion
manifest.Id.Should().Be(manifestRef.Id);
}
}
// Do collections too...
donaldgray marked this conversation as resolved.
Show resolved Hide resolved
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

namespace IIIF.Presentation.V3.Content;

public class Audio : ExternalResource, ITemporal, IPaintable
public class Sound : ExternalResource, ITemporal, IPaintable
{
public double? Duration { get; set; }

public Audio() : base("Sound")
public Sound() : base(nameof(Sound))
{
}
}
6 changes: 6 additions & 0 deletions src/IIIF/IIIF/Presentation/V3/Selectors/ISource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace IIIF.Presentation.V3.Selectors;

public class ISource
donaldgray marked this conversation as resolved.
Show resolved Hide resolved
{

}
7 changes: 7 additions & 0 deletions src/IIIF/IIIF/Presentation/V3/Selectors/SvgSelector.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace IIIF.Presentation.V3.Selectors;

public class SvgSelector : ISelector
donaldgray marked this conversation as resolved.
Show resolved Hide resolved
{
public string? Type => nameof(SvgSelector);
public string? Value { get; set; }
}
10 changes: 6 additions & 4 deletions src/IIIF/IIIF/Presentation/V3/SpecificResource.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
using IIIF.Presentation.V3.Selectors;
using Newtonsoft.Json;
using IIIF.Presentation.V3.Annotation;
using IIIF.Presentation.V3.Selectors;
using IIIF.Serialisation;

namespace IIIF.Presentation.V3;

public class SpecificResource : ResourceBase, IStructuralLocation
public class SpecificResource : ResourceBase, IStructuralLocation, IPaintable
{
public override string Type => nameof(SpecificResource);

[JsonProperty(Order = 101)] public string Source { get; set; }
[JsonConverter(typeof(SourceConverter))]
[JsonProperty(Order = 101)] public IPaintable Source { get; set; }

[JsonProperty(Order = 102)] public ISelector Selector { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class ExternalResourceConverter : ReadOnlyConverter<ExternalResource>
var type = jsonObject["type"].Value<string>();
var externalResource = type switch
{
nameof(Audio) => new Audio(),
nameof(Sound) => new Sound(),
nameof(Video) => new Video(),
nameof(Image) => new Image(),
_ => new ExternalResource(type)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ public class PaintableConverter : ReadOnlyConverter<IPaintable>

IPaintable? paintable = jsonObject["type"].Value<string>() switch
{
nameof(Audio) => new Audio(),
nameof(Sound) => new Sound(),
nameof(Video) => new Video(),
nameof(Image) => new Image(),
nameof(Canvas) => new Canvas(),
"Choice" => new PaintingChoice(),
nameof(SpecificResource) => new SpecificResource(),
_ => null
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public class ResourceBaseV3Converter : ReadOnlyConverter<ResourceBase>
nameof(Annotation) => new Annotation(),
nameof(AnnotationCollection) => new AnnotationCollection(),
nameof(AnnotationPage) => new AnnotationPage(),
nameof(Audio) => new Audio(),
nameof(Sound) => new Sound(),
nameof(Canvas) => new Canvas(),
nameof(Collection) => new Collection(),
nameof(Image) => new Image(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public class ResourceConverter : ReadOnlyConverter<IResource>
nameof(AuthAccessTokenService2) => new AuthAccessTokenService2(),
nameof(AuthLogoutService2) => new AuthLogoutService2(),
nameof(AuthProbeService2) => new AuthProbeService2(),
nameof(Audio) => new Audio(),
nameof(Sound) => new Sound(),
nameof(Video) => new Video(),
nameof(Image) => new Image(),
_ => null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ public class SelectorConverter : ReadOnlyConverter<ISelector>
{
nameof(AudioContentSelector) => new AudioContentSelector(),
nameof(ImageApiSelector) => new ImageApiSelector(),
"iiif:ImageApiSelector" => new ImageApiSelector(),
nameof(PointSelector) => new PointSelector(),
nameof(VideoContentSelector) => new VideoContentSelector()
nameof(VideoContentSelector) => new VideoContentSelector(),
nameof(SvgSelector) => new SvgSelector()
};

serializer.Populate(jsonObject.CreateReader(), selector);
Expand Down
57 changes: 57 additions & 0 deletions src/IIIF/IIIF/Serialisation/SourceConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;
using IIIF.Presentation.V3;
using IIIF.Presentation.V3.Annotation;
using IIIF.Presentation.V3.Content;
using IIIF.Utils;
using Newtonsoft.Json.Linq;

namespace IIIF.Serialisation;

public class SourceConverter : JsonConverter<IPaintable>
donaldgray marked this conversation as resolved.
Show resolved Hide resolved
{
public override IPaintable? ReadJson(JsonReader reader, Type objectType, IPaintable? existingValue, bool hasExistingValue,
JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
{
// We do not know that this is a Canvas...
// We would need knowledge of the rest of the IIIF Resource
return new Canvas { Id = reader.Value.ToString() };
}
else if (reader.TokenType == JsonToken.StartObject)
{
var obj = JObject.Load(reader);
var type = obj["type"].Value<string>();
IPaintable paintable = type switch
{
nameof(Sound) => new Sound(),
nameof(Video) => new Video(),
nameof(Image) => new Image(),
nameof(Canvas) => new Canvas(),
nameof(SpecificResource) => new SpecificResource()
};
serializer.Populate(obj.CreateReader(), paintable);
return paintable;
}

return null;
}

public override void WriteJson(JsonWriter writer, IPaintable? value, JsonSerializer serializer)
{
if (value is Canvas canvas && (canvas.SerialiseTargetAsId || IsSimpleCanvas(canvas)))
{
writer.WriteValue(canvas.Id);
return;
}

// Default, pass through behaviour:
JObject.FromObject(value, serializer).WriteTo(writer);
}

private static bool IsSimpleCanvas(Canvas canvas)
{
return canvas.Width == null && canvas.Duration == null && canvas.Items.IsNullOrEmpty();
}

}
10 changes: 6 additions & 4 deletions src/IIIF/IIIF/Serialisation/TargetConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@ public class TargetConverter : JsonConverter<IStructuralLocation>
var obj = JObject.Load(reader);

var type = obj["type"].Value<string>();
return type switch
IStructuralLocation structuralLocation = type switch
{
nameof(Canvas) => obj.ToObject<Canvas>(),
nameof(Range) => obj.ToObject<Range>(),
nameof(SpecificResource) => obj.ToObject<SpecificResource>()
nameof(Canvas) => new Canvas(),
nameof(Range) => new Range(),
nameof(SpecificResource) => new SpecificResource()
};
serializer.Populate(obj.CreateReader(), structuralLocation);
donaldgray marked this conversation as resolved.
Show resolved Hide resolved
return structuralLocation;
}

return null;
Expand Down
Loading