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

SF-3184 Add USJ support to the draft API #2988

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
27 changes: 27 additions & 0 deletions src/SIL.Converters.Usj/IUsj.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Collections;

namespace SIL.Converters.Usj
{
/// <summary>
/// An interface for Unified Scripture JSON (USJ) types.
/// </summary>
public interface IUsj
{
/// <summary>
/// The JSON representation of scripture contents from USFM/USX.
/// </summary>
/// <value>This will either be a <see cref="UsjMarker"/> or <see cref="string"/>.</value>
/// <remarks>Nullable. The contents will be laid out in order.</remarks>
ArrayList Content { get; set; }

/// <summary>
/// The USJ spec type.
/// </summary>
string Type { get; set; }

/// <summary>
/// The USJ spec version.
/// </summary>
string Version { get; set; }
}
}
13 changes: 13 additions & 0 deletions src/SIL.Converters.Usj/LowerCaseNamingStrategy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Newtonsoft.Json.Serialization;

namespace SIL.Converters.Usj
{
/// <summary>
/// Ensures that the JSON properties for the USJ data model are in lower case.
/// </summary>
internal class LowerCaseNamingStrategy : NamingStrategy
{
/// <inheritdoc />
protected override string ResolvePropertyName(string name) => name.ToLowerInvariant();
}
}
11 changes: 11 additions & 0 deletions src/SIL.Converters.Usj/SIL.Converters.Usj.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>

</Project>
24 changes: 24 additions & 0 deletions src/SIL.Converters.Usj/Usj.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace SIL.Converters.Usj
{
/// <summary>
/// Unified Scripture JSON (USJ) - The JSON variant of USFM and USX data models.
/// These types follow this schema: <c>https://github.com/usfm-bible/tcdocs/blob/usj/grammar/usj.js</c>
/// </summary>
public class Usj : UsjBase, IUsj
{
/// <summary>
/// The supported USJ spec type.
/// </summary>
public static readonly string UsjType = "USJ";

/// <summary>
/// The supported USJ spec version.
/// </summary>
public static readonly string UsjVersion = "3.1";

/// <summary>
/// The USJ spec version.
/// </summary>
public string Version { get; set; }
}
}
43 changes: 43 additions & 0 deletions src/SIL.Converters.Usj/UsjBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Collections;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace SIL.Converters.Usj
{
/// <summary>
/// Elements shared between <see cref="Usj"/> and <see cref="UsjMarker"/>.
/// </summary>
[JsonObject(NamingStrategyType = typeof(LowerCaseNamingStrategy), ItemNullValueHandling = NullValueHandling.Ignore)]
public abstract class UsjBase
{
/// <summary>
/// For <see cref="Usj"/>, this is the USJ spec type.
/// For <see cref="UsjMarker"/>, this is the kind/category of node or element this is,
/// corresponding the USFM marker and USX node.
/// </summary>
/// <example><c>para</c>, <c>verse</c>, <c>char</c>.</example>
public string Type { get; set; }

/// <summary>
/// The JSON representation of scripture contents from USFM/USX.
/// </summary>
/// <value>This will either be a <see cref="UsjMarker"/> or <see cref="string"/>.</value>
/// <remarks>
/// Nullable.
/// If there are no contents, this will be null for <see cref="UsjMarker"/>, or empty for <see cref="Usj"/>.
/// The contents will be laid out in order.
/// </remarks>
[JsonConverter(typeof(UsjContentConverter))]
public ArrayList Content { get; set; }

/// <summary>
/// Additional attributes that are not a part of the USJ specification.
/// This is only used for <see cref="UsjMarker"/>.
/// </summary>
/// <remarks>
/// These are typically <c>closed</c>, <c>colspan</c>, etc.
/// </remarks>
[JsonExtensionData]
public Dictionary<string, object> AdditionalData { get; } = new Dictionary<string, object>();
}
}
61 changes: 61 additions & 0 deletions src/SIL.Converters.Usj/UsjContentConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System;
using System.Collections;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace SIL.Converters.Usj
{
/// <summary>
/// Converts the contents of the Content <see cref="ArrayList"/> to and from JSON, preserving
/// the two supported content types: <see cref="string"/> and <see cref="UsjMarker"/>.
/// </summary>
public class UsjContentConverter : JsonConverter<ArrayList>
{
/// <inheritdoc />
public override ArrayList ReadJson(
JsonReader reader,
Type objectType,
ArrayList existingValue,
bool hasExistingValue,
JsonSerializer serializer
)
{
var jArray = JArray.Load(reader);
var list = new ArrayList();

foreach (JToken item in jArray)
{
if (item.Type == JTokenType.String)
{
list.Add(item.ToString());
}
else
{
var usjMarker = item.ToObject<UsjMarker>();
list.Add(usjMarker);
}
}

return list;
}

/// <inheritdoc />
public override void WriteJson(JsonWriter writer, ArrayList value, JsonSerializer serializer)
{
JArray jArray = new JArray();
foreach (object item in value)
{
if (item is string str)
{
jArray.Add(str);
}
else if (item is UsjMarker usjMarker)
{
jArray.Add(JToken.FromObject(usjMarker));
}
}

jArray.WriteTo(writer);
}
}
}
73 changes: 73 additions & 0 deletions src/SIL.Converters.Usj/UsjMarker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
namespace SIL.Converters.Usj
{
/// <summary>
/// A Scripture Marker and its contents.
/// </summary>
public class UsjMarker : UsjBase
{
/// <summary>
/// The corresponding marker in USFM or style in USX.
/// </summary>
/// <example><c>p</c>, <c>v</c>, <c>nd</c>.</example>
public string Marker { get; set; }

/// <summary>
/// The milestone start ID, which indicates the Book-chapter-verse value in the paragraph based structure.
/// </summary>
/// <remarks>Nullable.</remarks>
public string Sid { get; set; }

/// <summary>
/// Milestone end ID, which matches the milestone start ID <see cref="Sid"/>.
/// <see cref="Eid"/> is not specified in the USJ spec, but is kept for USX compatibility.
/// </summary>
/// <remarks>Nullable.</remarks>
public string Eid { get; set; }

/// <summary>
/// Chapter number or verse number.
/// </summary>
/// <remarks>Nullable.</remarks>
public string Number { get; set; }

/// <summary>
/// The 3-letter book code in ID element.
/// </summary>
/// <remarks>Nullable.</remarks>
public string Code { get; set; }

/// <summary>
/// Alternate chapter number or verse number.
/// </summary>
/// <remarks>Nullable.</remarks>
public string AltNumber { get; set; }

/// <summary>
/// Published character of a chapter or verse.
/// </summary>
/// <value>
/// This can be a letter (I, II, etc.), a number (1, 2, ...), or both.
/// It is only displayed in the published version of the scripture text.
/// </value>
/// <remarks>Nullable.</remarks>
public string PubNumber { get; set; }

/// <summary>
/// Caller character for footnotes and cross-refs.
/// </summary>
/// <remarks>Nullable.</remarks>
public string Caller { get; set; }

/// <summary>
/// Alignment of table cells.
/// </summary>
/// <remarks>Nullable.</remarks>
public string Align { get; set; }

/// <summary>
/// Category of extended study bible sections.
/// </summary>
/// <remarks>Nullable.</remarks>
public string Category { get; set; }
}
}
Loading
Loading