Skip to content

Commit 714d684

Browse files
authored
feature/add-collection-expression-support (#36)
* Add support for collection expressions * Add test cases * Optimize SchemaSerializer
1 parent b3f66ea commit 714d684

38 files changed

+3169
-1144
lines changed

Benchmarks/.editorconfig

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,9 @@ dotnet_diagnostic.IDE0005.severity = none
1010

1111
# CA1515: Consider making public types internal
1212
# Justification: Won't be exposed in the NuGet packages
13-
dotnet_diagnostic.CA1515.severity = none
13+
dotnet_diagnostic.CA1515.severity = none
14+
15+
# CA1822: Mark members as static
16+
# Justification: Benchmark methods shouldn't be static
17+
dotnet_diagnostic.CA1822.severity = none
18+
dotnet_diagnostic.CA1707.severity = none
Lines changed: 49 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
namespace Schema.NET.Benchmarks.Core;
22

33
using System;
4-
using System.Collections.Generic;
54

65
public class BookBenchmark : SchemaBenchmarkBase
76
{
@@ -15,66 +14,66 @@ public override Thing InitialiseThing() =>
1514
Name = "J.D. Salinger",
1615
},
1716
Url = new Uri("https://www.barnesandnoble.com/store/info/offer/JDSalinger"),
18-
WorkExample = new List<ICreativeWork?>
19-
{
20-
new Book
17+
WorkExample =
18+
[
19+
new Book
20+
{
21+
Isbn = "031676948",
22+
BookEdition = "2nd Edition",
23+
BookFormat = BookFormatType.Hardcover,
24+
PotentialAction = new ReadAction
2125
{
22-
Isbn = "031676948",
23-
BookEdition = "2nd Edition",
24-
BookFormat = BookFormatType.Hardcover,
25-
PotentialAction = new ReadAction
26+
Target = new EntryPoint
2627
{
27-
Target = new EntryPoint
28-
{
29-
UrlTemplate = "https://www.barnesandnoble.com/store/info/offer/0316769487?purchase=true",
30-
ActionPlatform = new List<Uri?>
31-
{
32-
new Uri("https://schema.org/DesktopWebPlatform"),
33-
new Uri("https://schema.org/IOSPlatform"),
34-
new Uri("https://schema.org/AndroidPlatform"),
35-
},
36-
},
37-
ExpectsAcceptanceOf = new Offer
28+
UrlTemplate = "https://www.barnesandnoble.com/store/info/offer/0316769487?purchase=true",
29+
ActionPlatform =
30+
[
31+
new Uri("https://schema.org/DesktopWebPlatform"),
32+
new Uri("https://schema.org/IOSPlatform"),
33+
new Uri("https://schema.org/AndroidPlatform"),
34+
],
35+
},
36+
ExpectsAcceptanceOf = new Offer
37+
{
38+
Price = 6.99M,
39+
PriceCurrency = "USD",
40+
EligibleRegion = new Country
3841
{
39-
Price = 6.99M,
40-
PriceCurrency = "USD",
41-
EligibleRegion = new Country
42-
{
43-
Name = "US",
44-
},
45-
Availability = ItemAvailability.InStock,
42+
Name = "US",
4643
},
44+
Availability = ItemAvailability.InStock,
4745
},
4846
},
49-
new Book
47+
},
48+
new Book
49+
{
50+
Isbn = "031676947",
51+
BookEdition = "1st Edition",
52+
BookFormat = BookFormatType.EBook,
53+
PotentialAction = new ReadAction
5054
{
51-
Isbn = "031676947",
52-
BookEdition = "1st Edition",
53-
BookFormat = BookFormatType.EBook,
54-
PotentialAction = new ReadAction
55+
Target = new EntryPoint
5556
{
56-
Target = new EntryPoint
57-
{
58-
UrlTemplate = "https://www.barnesandnoble.com/store/info/offer/031676947?purchase=true",
59-
ActionPlatform = new List<Uri?>
60-
{
61-
new Uri("https://schema.org/DesktopWebPlatform"),
62-
new Uri("https://schema.org/IOSPlatform"),
63-
new Uri("https://schema.org/AndroidPlatform"),
64-
},
65-
},
66-
ExpectsAcceptanceOf = new Offer
57+
UrlTemplate = "https://www.barnesandnoble.com/store/info/offer/031676947?purchase=true",
58+
ActionPlatform =
59+
[
60+
new Uri("https://schema.org/DesktopWebPlatform"),
61+
new Uri("https://schema.org/IOSPlatform"),
62+
new Uri("https://schema.org/AndroidPlatform"),
63+
],
64+
},
65+
ExpectsAcceptanceOf = new Offer
66+
{
67+
Price = 1.99M,
68+
PriceCurrency = "USD",
69+
EligibleRegion = new Country
6770
{
68-
Price = 1.99M,
69-
PriceCurrency = "USD",
70-
EligibleRegion = new Country
71-
{
72-
Name = "UK",
73-
},
74-
Availability = ItemAvailability.InStock,
71+
Name = "UK",
7572
},
73+
Availability = ItemAvailability.InStock,
7674
},
7775
},
78-
},
76+
},
77+
],
7978
};
8079
}

README.md

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
1-
[![Schema.NET NuGet Package](https://img.shields.io/nuget/v/SchemaDotNet.svg)](https://www.nuget.org/packages/SchemaDotNet)
1+
# (Temporary?) Fork of [Schema.NET](https://github.com/RehanSaeed/Schema.NET)
2+
### Changes:
3+
- Add targets for .NET 8 - 10
4+
- Add support for [collection expressions](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/collection-expressions)
5+
- Performance improvements
6+
### Why the fork?
7+
> I created this fork for to use in a large webshop, I initially wanted the package to target .net 8.<br/>
8+
> But once begun, I added support for collection expressions, and other small tweaks.
9+
10+
<hr/>
11+
12+
[![SchemaDotNet NuGet Package](https://img.shields.io/nuget/v/SchemaDotNet.svg)](https://www.nuget.org/packages/SchemaDotNet)
213
[![build](https://github.com/TimmyMC/Schema.NET/actions/workflows/build.yml/badge.svg)](https://github.com/TimmyMC/Schema.NET/actions/workflows/build.yml)
3-
4-
[//]: # ([![Schema.NET NuGet Package Downloads]&#40;https://img.shields.io/nuget/dt/SchemaDotNet&#41;]&#40;https://www.nuget.org/packages/SchemaDotNet&#41;)
14+
[![SchemaDotNet NuGet Package Downloads](https://img.shields.io/nuget/dt/SchemaDotNet)](https://www.nuget.org/packages/SchemaDotNet)
515

616
Schema.org objects turned into strongly typed C# POCO classes for use in .NET. All classes can be serialized into JSON/JSON-LD and XML, typically used to represent structured data in the `head` section of `html` page.
717

@@ -33,7 +43,7 @@ If writing the result into a `<script>` element, be sure to use the `.ToHtmlEsca
3343

3444
## What is Schema.org?
3545

36-
[schema.org](https://schema.org) defines a set of standard classes and their properties for objects and services in the real world. This machine readable format is a common standard used across the web for describing things.
46+
[schema.org](https://schema.org) defines a set of standard classes and their properties for objects and services in the real world. This machine-readable format is a common standard used across the web for describing things.
3747

3848
## Where is Schema.org Used?
3949

@@ -77,7 +87,7 @@ Windows UWP apps let you share data using schema.org classes. [Here](https://doc
7787

7888
schema.org defines classes and properties, where each property can have a single value or an array of multiple values. Additionally, properties can have multiple types e.g. an `Address` property could have a type of `string` or a type of `PostalAddress` which has it's own properties such as `StreetAddress` or `PostalCode` which breaks up an address into it's constituent parts.
7989

80-
To facilitate this Schema.NET uses some clever C# generics and implicit type conversions so that setting a single or multiple values is possible and that setting a `string` or `PostalAddress` is also possible:
90+
To facilitate this SchemaDotNet uses some clever C# generics and implicit type conversions so that setting a single or multiple values is possible and that setting a `string` or `PostalAddress` is also possible:
8191

8292
```C#
8393
// Single string address
@@ -89,11 +99,11 @@ var organization = new Organization()
8999
// Multiple string addresses
90100
var organization = new Organization()
91101
{
92-
Address = new List<string>()
93-
{
102+
Address =
103+
[
94104
"123 Old Kent Road E10 6RL",
95105
"456 Finsbury Park Road SW1 2JS"
96-
}
106+
]
97107
};
98108

99109
// Single PostalAddress address
@@ -109,8 +119,8 @@ var organization = new Organization()
109119
// Multiple PostalAddress addresses
110120
var organization = new Organization()
111121
{
112-
Address = new List<PostalAddress>()
113-
{
122+
Address =
123+
[
114124
new PostalAddress()
115125
{
116126
StreetAddress = "123 Old Kent Road",
@@ -121,17 +131,17 @@ var organization = new Organization()
121131
StreetAddress = "456 Finsbury Park Road",
122132
PostalCode = "SW1 2JS"
123133
}
124-
}
134+
]
125135
};
126136

127137
// Mixed Author types
128138
var book = new Book()
129139
{
130-
Author = new List<object>()
131-
{
140+
Author =
141+
[
132142
new Organization() { Name = "Penguin" },
133143
new Person() { Name = "J.D. Salinger" }
134-
}
144+
]
135145
};
136146

137147
// Deconstruct a property containing mixed types
@@ -145,18 +155,11 @@ This magic is all carried out using [implicit conversion operators](https://docs
145155

146156
## More Examples
147157

148-
For more examples and actual running code samples, take a look at the unit tests in the project source code.
149-
150-
## Schema.NET.Pending
151-
152-
There are many pending types on [schema.org](https://schema.org) which are not yet fully formed and ready for production. If you need to use these, you can install the [Schema.NET.Pending](https://www.nuget.org/packages/SchemaDotNet.Pending) NuGet package instead of [Schema.NET](https://www.nuget.org/packages/SchemaDotNet). This package contains all released schema types as well as all pending types.
153-
154-
[![Schema.NET.Pending NuGet Package](https://img.shields.io/nuget/v/Schema.NET.Pending.svg)](https://www.nuget.org/packages/SchemaDotNet.Pending)
158+
For more examples and actual running code samples, take a look at the [unit tests](https://github.com/TimmyMC/Schema.NET/tree/main/Tests/Schema.NET.Test/Examples) in the project source code.
155159

156-
[//]: # ([![Schema.NET.Pending NuGet Package Downloads]&#40;https://img.shields.io/nuget/dt/Schema.NET.Pending&#41;]&#40;https://www.nuget.org/packages/SchemaDotNet.Pending&#41;)
160+
## SchemaDotNet.Pending
157161

158-
## Continuous Integration
162+
There are many pending types on [schema.org](https://schema.org) which are not yet fully formed and ready for production. If you need to use these, you can install the [SchemaDotNet.Pending](https://www.nuget.org/packages/SchemaDotNet.Pending) NuGet package instead of [SchemaDotNet](https://www.nuget.org/packages/SchemaDotNet). This package contains all released schema types as well as all pending types.
159163

160-
| Name | Operating System | Status | History |
161-
| :--- | :--- | :--- |:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
162-
| GitHub Actions | Ubuntu, Mac & Windows | [![GitHub Actions Status](https://github.com/TimmyMC/Schema.NET/workflows/Build/badge.svg?branch=main)](https://github.com/TimmyMC/Schema.NET/actions) | [![GitHub Actions Build History](https://buildstats.info/github/chart/TimmyMC/Schema.NET?branch=main&includeBuildsFromPullRequest=false)](https://github.com/TimmyMC/Schema.NET/actions) |
164+
[![SchemaDotNet.Pending NuGet Package](https://img.shields.io/nuget/v/SchemaDotNet.Pending.svg)](https://www.nuget.org/packages/SchemaDotNet.Pending)
165+
[![SchemaDotNet.Pending NuGet Package Downloads](https://img.shields.io/nuget/dt/SchemaDotNet.Pending)](https://www.nuget.org/packages/SchemaDotNet.Pending)

Source/.editorconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@
66
# CA1716: Identifiers should not match keywords
77
# Justification: Ignore Parameters to avoid breaking changes
88
dotnet_code_quality.CA1716.analyzed_symbol_kinds = Namespace, NamedType, Method, Property, Event
9+
10+
# CA1710: Identifiers should have correct suffix
11+
dotnet_code_quality.CA1710.additional_required_suffixes = IEnumerable->{}|IReadOnlyCollection->{}|IValues->Values

Source/Common/IValues.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@
55
/// <summary>
66
/// An <see cref="IEnumerable"/> with a count.
77
/// </summary>
8-
#pragma warning disable CA1710 // Identifiers should have correct suffix
98
#pragma warning disable CA1010 // Collections should implement generic interface
109
public interface IValues : IEnumerable
1110
#pragma warning restore CA1010 // Collections should implement generic interface
12-
#pragma warning restore CA1710 // Identifiers should have correct suffix
1311
{
1412
/// <summary>
1513
/// Gets the count.

Source/Common/OneOrMany{T}.cs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@ namespace Schema.NET;
55
using System.Collections.Generic;
66
using System.Diagnostics.CodeAnalysis;
77
using System.Linq;
8+
using System.Runtime.CompilerServices;
89

910
/// <summary>
1011
/// A single or list of values.
1112
/// </summary>
1213
/// <typeparam name="T">The type of the values.</typeparam>
13-
/// <seealso cref="ICollection{T}" />
14+
/// <seealso cref="IReadOnlyCollection{T}" />
15+
[CollectionBuilder(typeof(OneOrManyBuilder), nameof(OneOrManyBuilder.Create))]
1416
#pragma warning disable CA1710 // Identifiers should have correct suffix.
15-
public readonly struct OneOrMany<T>
17+
public readonly struct OneOrMany<T> : IReadOnlyCollection<T>, IValues, IEquatable<OneOrMany<T>>
1618
#pragma warning restore CA1710 // Identifiers should have correct suffix.
17-
: IReadOnlyCollection<T>, IValues, IEquatable<OneOrMany<T>>
1819
{
1920
private readonly T[]? collection;
2021

@@ -40,7 +41,7 @@ public OneOrMany(T? item)
4041
/// Initializes a new instance of the <see cref="OneOrMany{T}"/> struct.
4142
/// </summary>
4243
/// <param name="span">The span of values.</param>
43-
public OneOrMany(ReadOnlySpan<T?> span)
44+
public OneOrMany(params ReadOnlySpan<T?> span)
4445
{
4546
if (!span.IsEmpty)
4647
{
@@ -49,9 +50,8 @@ public OneOrMany(ReadOnlySpan<T?> span)
4950

5051
if (typeof(T) == typeof(string))
5152
{
52-
for (var i = 0; i < span.Length; i++)
53+
foreach (var item in span)
5354
{
54-
var item = span[i];
5555
if (!string.IsNullOrWhiteSpace(item as string))
5656
{
5757
items[index] = item;
@@ -61,9 +61,8 @@ public OneOrMany(ReadOnlySpan<T?> span)
6161
}
6262
else
6363
{
64-
for (var i = 0; i < span.Length; i++)
64+
foreach (var item in span)
6565
{
66-
var item = span[i];
6766
if (item is not null)
6867
{
6968
items[index] = item;
@@ -93,12 +92,6 @@ public OneOrMany(ReadOnlySpan<T?> span)
9392
this.HasOne = false;
9493
}
9594

96-
/// <summary>
97-
/// Initializes a new instance of the <see cref="OneOrMany{T}"/> struct.
98-
/// </summary>
99-
/// <param name="array">The array of values.</param>
100-
public OneOrMany(params T?[] array) : this(array.AsSpan()) { }
101-
10295
/// <summary>
10396
/// Initializes a new instance of the <see cref="OneOrMany{T}"/> struct.
10497
/// </summary>
@@ -210,9 +203,9 @@ public IEnumerator<T> GetEnumerator()
210203
{
211204
if (this.collection is not null)
212205
{
213-
for (var i = 0; i < this.collection.Length; i++)
206+
foreach (var item in this.collection)
214207
{
215-
yield return this.collection[i];
208+
yield return item;
216209
}
217210
}
218211
}
@@ -294,3 +287,15 @@ public bool Equals(OneOrMany<T> other)
294287
/// <returns>A <see cref="ReadOnlySpan{T}"/> wrapping the current items.</returns>
295288
public ReadOnlySpan<T> AsSpan() => this.collection.AsSpan();
296289
}
290+
291+
/// <summary>
292+
/// Contains the generic static constructors needed to support collection expressions.
293+
/// </summary>
294+
public static class OneOrManyBuilder
295+
{
296+
/// <summary>
297+
/// Initializes a new instance of the <see cref="OneOrMany{T}"/> struct.
298+
/// </summary>
299+
/// <param name="items">The span of values.</param>
300+
public static OneOrMany<T> Create<T>(ReadOnlySpan<T> items) => new(items);
301+
}

0 commit comments

Comments
 (0)