Skip to content

Commit

Permalink
SF-3184 Add USJ conversion support to and from USX
Browse files Browse the repository at this point in the history
  • Loading branch information
pmachapman committed Feb 3, 2025
1 parent c463df6 commit 69e80ba
Show file tree
Hide file tree
Showing 16 changed files with 1,219 additions and 0 deletions.
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 const string UsjType = "USJ";

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

/// <summary>
/// The USJ spec version.
/// </summary>
public string Version { get; set; }
}
}
39 changes: 39 additions & 0 deletions src/SIL.Converters.Usj/UsjBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
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. 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);
}
}
}
68 changes: 68 additions & 0 deletions src/SIL.Converters.Usj/UsjMarker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
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>
/// Indicates the Book-chapter-verse value in the paragraph based structure.
/// </summary>
/// <remarks>Nullable.</remarks>
public string Sid { get; set; }

/// <summary>
/// Milestone end ID, matches start ID (not currently included in USJ spec).
/// </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 chapter or verse.
/// </summary>
/// <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

0 comments on commit 69e80ba

Please sign in to comment.