Skip to content

Latest commit

 

History

History
459 lines (320 loc) · 22.2 KB

CHANGES.md

File metadata and controls

459 lines (320 loc) · 22.2 KB

Latest Changes

Changes after v3.0.0

Please see release notes for all versions after v3.0.0 on https://github.com/axuno/SmartFormat/releases/

v3.0.0

You'll find a detailed description for all changes including code samples in the Wiki.

Significant boost in performance

After implementing a zero allocation ValueStringBuilder (#193, #228) and Object Pools (#229) for all classes which are frequently instantiated:

  • Parsing is 10% faster with 50-80% less GC and memory allocation
  • Formatting is up to 40% faster with 50% less GC and memory allocation

Sample of BenchmarkDotNet results under NetStandard2.1:

|         Method |     N |     Mean |   Error |  StdDev |      Gen 0 |     Gen 1 | Gen 2 | Allocated |
|--------------- |------ |---------:|--------:|--------:|-----------:|----------:|------:|----------:|
SmartFormat v2.7.2 (only SingleThread)
| Format         | 10000 | 223.9 ms | 1.48 ms | 1.38 ms | 21333.3333 |         - |     - |    172 MB |
SmartFormat v3.0.0
| SingleThread   | 10000 | 108.2 ms | 0.52 ms | 0.49 ms |  3200.0000 |         - |     - |     26 MB |
| ThreadSafe     | 10000 | 128.0 ms | 1.29 ms | 1.21 ms |  6000.0000 |         - |     - |     48 MB |

string.Format Compatibility (#172, #173, #175)

The main point about string.Format compatibility is, how curly braces and colons are processed in the format string.

In most cases string.Format compatibility does not bring any advantages.

With SmartSettings.StringFormatCompatibility = true SmartFormat is fully compatible. The downside, however, ist that custom formatter extensions cannot be parsed and used with this setting.

With SmartSettings.StringFormatCompatibility = false (default), all features of SmartFormat are available.

Reasoning: The distinction was necessary because of syntax conflicts between SmartFormat extensions and string.Format. It brings a more concise and clear set of formatting rules and full string.Format compatibility even in "edge cases".

Nullable Notation

  • C# like nullable notation allows to display Nullable<T> types safely (#176).

  • The SmartFormat notation is "{SomeNullable?.Property}". If SomeNullable is null, the expression is evaluated as string.Empty. "{SomeNullable.Property}" would throw a FormattingException.

Thread-Safe Caching (#229)

Object Pools, Reflection and other caches can operate in thread-safe mode. (SmartFormatters and Parsers still require one instance per thread.)

No Limits for Allowed Characters

  • This was a limitation in v2. In v3, the Parser can parse any character as part of formatter options. This means e.g. no limitations for RegEx expressions used in IsMatchFormatter. Note: Special characters like (){}:\ must be escaped with \.

  • Literals may contain any Unicode characters (#166). Add unicode escape characters like "\u1234". Thanks to @karljj1.

  • Global support for Alignment (#174): In v2, Alignment of output values was limited to the DefaultFormatter. It's about the equivalent to e.g. string.Format("{0,10}"). Alignment is now supported by all IFormatters.

  • Full control about output characters, including whitespace.

Improved parsing of HTML input

Introduced bool ParserSettings.ParseInputAsHtml. The default is false.

If true, theParser will parse all content inside <script> and <style> tags as LiteralText. All other places may still contain Placeholders.

This is because <script> and <style> tags may contain curly or square braces, that interfere with the SmartFormat {Placeholder}.

Improved Extensions

Split character for options and formats

The character to split options and formats can be changed. This allows having the default split character | as part of the output string.

Affects ChooseFormatter, ConditionalFormatter, IsMatchFormatter, ListFormatter, PluralLocalizationFormatter, SubStringFormatter.

ReflectionSource

Added a type cache which increases speed by factor 4. Thanks to @karljj1. (#155)

DictionarySource

Speed increased by 10% with less GC pressure (#189).

IsMatchFormatter

The IsMatchFormatter is a formatter with evaluation of regular expressions. It allows to control its output depending on a RegEx match.

New: The formatter can output matching group values of a RegEx (#245).

PluralLocalizationFormatter (#209)

  • Constructor with string argument for default language is removed.
  • Property DefaultTwoLetterISOLanguageName is removed.
  • CultureInfo.InvariantCulture maps to CultureInfo.GetCultureInfo("en") (#243).

Culture is now determined in this sequence (same as with LocalizationFormatter):
a) Get the culture from the FormattingInfo.FormatterOptions.
b) Get the culture from the IFormatProvider argument (which may be a CultureInfo) to SmartFormatter.Format(IFormatProvider, string, object?[])
c) The CultureInfo.CurrentUICulture

TimeFormatter (#220, #221, #234)

  • Constructor with string argument for default language is removed.
  • Property DefaultTwoLetterISOLanguageName is removed.

Culture is now determined in this sequence (same as with LocalizationFormatter):
a) Get the culture from the FormattingInfo.FormatterOptions.
b) Get the culture from the IFormatProvider argument (which may be a CultureInfo) to SmartFormatter.Format(IFormatProvider, string, object?[])
c) The CultureInfo.CurrentUICulture

Extended CommonLanguagesTimeTextInfo, which now includes French, Spanish, Portuguese, Italian and German as new languages besides English out-of-the-box.

This notation - using formats as formatter options - was allowed in SmartFormat v2.x, but is now depreciated. It is still detected and working, as long as the format part is left empty.

var formatDepreciated = "{0:time(abbr hours noless)}";

This format string is recommended for SmartFormat v3 and later. It allows for including the language as an option to the TimeFormatter:

// Without language option:
var formatRecommended = "{0:time:abbr hours noless:}";
// With language option:
var formatRecommended = "{0:time(en):abbr hours noless:}";

SubStringFormatter

The formatter now accecpts a format argument with a nested Placeholder that lets you format the result of the sub-string operation (#258).

Example: Convert the sub-string to lower-case:

Smart.Format("{0:substr(0,2):{ToLower}}", "ABC");

ChooseFormatter #253

Modified ChooseFormatter case-sensitivity for option strings. This modification is compatible with v2:

  • bool and null as string: always case-insensitive
  • using SmartSettings.CaseSensitivity unless overridden with ChooseFormatter.CaseSensitivity
  • option strings comparison is culture-aware

Added Extensions

StringSource

The StringSource takes over a part of the functionality, which has been implemented in ReflectionSource in v2. Compared to reflection with caching, speed is 20% better at 25% less memory allocation. (#178, #216)

KeyValuePairSource (#244)

The KeyValuePairSource is a simple, cheap and performant way to create named placeholders.

JSON support

Separation of JsonSource into 2 ISource extensions (#177, #201):

  • NewtonSoftJsonSource
  • SystemTextJsonSource

PersistentVariableSource and GlobalVariableSource

Both provide global variables that are stored in VariablesGroup containers. These variables are not passed in as arguments when formatting a string. Instead, they are taken from one of these two registered (global) ISources. (#233)

Credits to Needle and their PersistentVariablesSource extension to SmartFormat.

NullFormatter

In the context of Nullable Notation (see below), the NullFormatter has been added. It outputs a custom string literal, if the variable is null, else another literal (default is string.Empty) or a nested Placeholder. (#176, #199)

LocalizationFormatter

  • Added LocalizationFormatter to localize literals and placeholders (#207).

  • Added ILocalizationProvider and a standard implemention as LocalizationProvider, which handles resx resource files. A fallback culture can be set. LocalizationProvider can search an unlimited number of defined resoures.

Miscellaneous

Formatter Name

IFormatters have one single, unique name (#185). In v2, IFormatters could have an unlimited number of names.

IInitializer Interface

Any (custom) ISource and IFormatter can implement IInitializer. Then, the SmartFormatter will call Initialize(SmartFormatter smartFormatter) of the extension, before adding it to the extension list (#180).

New parameter type for SmartFormatter.Format(...)

Added support for IList<object> parameters to the SmartFormatter (thanks to @karljj1) (#154)

SmartObjects

Removed obsolete SmartObjects (which have been replaced by ValueTuple) (092b7b1)

Parsing HTML input

Introduced experimental bool ParserSettings.ParseInputAsHtml. The default is false.

If true, theParser will parse all content inside <script> and <style> tags as LiteralText. This is because <script> and <style> tags may contain curly or square braces, that interfere with the SmartFormat {Placeholder}.

All other places may still contain Placeholders. (#203)

SmartFormat Packages

SmartFormat.NET

This is a package which references all packages below.

SmartFormat

SmartFormat is the core package. It comes with the most frequently used extensions built-in.

SmartFormat.Extensions.System.Text.Json

This package is a SmartFormat extension for formatting System.Text.Json types as a source.

SmartFormat.Extensions.Newtonsoft.Json

This package is a SmartFormat extension for formatting Newtonsoft.Json types as a source.

SmartFormat.Extensions.Xml

This package is a SmartFormat extension for reading and formatting System.Xml.Linq.XElements.

SmartFormat.Extensions.Time

This package is a SmartFormat extension for formatting System.DateTime, System.DateTimeOffset and System.TimeSpan types.


v2 Releases


v2.7.3

  • Hot fix: Newtonsoft.Json prior to version 13.0.1 is vulnerable. The minimum version of the package reference is now 13.0.1 Newtonsoft.Json prior to version 13.0.1 is vulnerable to Insecure Defaults due to improper handling of expressions with high nesting level that lead to StackOverFlow exception or high CPU and RAM usage. Exploiting this vulnerability results in Denial Of Service (DoS).

v2.7.2

  • Fixed: ConditionalFormatter processes unsigned numbers in arguments correctly.
  • Fixed: JsonSource: Corrected handling of null values in Newtonsoft.Json objects.

v2.7.1

  • Fixed: #179 DualFromZeroToTwo plural rule. Thanks to @OhSoGood
  • Fixed: #211 Illegal placeholder characters that are not 8-bit, will no more throw unexpected ThrowByteOverflowException. Thanks to @bogatykh

v2.7.0

  • Fixed broken backward compatibilty introduced in v2.6.2 (issues referenced in #148, #147, #143).
  • Fixed: Take an erroneous format string like "this is {uncomplete" (missing closing brace). Before v2.7.0 the parser handled {uncomplete as a TextLiteral, not as an erroneous Placeholder.
  • Fixed: Since v1.6.1 there was an undiscovered issue: If the Parser encountered a ParsingError.TooManyClosingBraces, this closing brace was simply "swallowed-up". This way, the result with Parser.ErrorAction.MaintainTokens differs from the original format string. From v2.7.0, the redundant closing brace is handled as a TextLiteral.
  • Improved: For ParsingError.TrailingOperatorsInSelector and ParsingError.InvalidCharactersInSelector the causing character is now included in the Exception.Message.
  • If you have issues formatting HTML with CSS and/or JavaScript included, please read the bullet-proof How-to in the Wiki

v2.6.2

  • Fix: Fully implemented all Settings.ParseErrorAction, see #143 - Thanks to Anders Jonsson

v2.6.1

  • Fixed #136
  • Upgraded test project to netcoreapp3.1
  • Enhanced SubString extension as described in PR142 - Thanks to Anders Jonsson

v2.6.0

  • Migrated project with Nullable Reference Types (NRT) enabled

v2.5.3.0

Bugfix: ListFormatter will now process IList data sources only.

v2.5.2.0

Supported frameworks now are:

  • .Net Framework 4.6.1, 4.6.2, 4.7.2 and 4.8 (System.Text.Json is not supported for .Net Framework 4.5.x and thus had to be dropped)
  • .Net Standard 2.0 and 2.1

v2.5.1.0

  • Added System.Text.Json.JsonElement to the JsonSource extension. Newtonsoft.Json is still included.
  • Added a demo version as a net5.0 WindowsDesktop App
  • Supported framworks now are:
    • .Net Framework 4.6.1, 4.7.2 and 4.8 (System.Text.Json is not supported for .Net Framework 4.5.x and thus had to be dropped)
    • .Net Standard 2.0 and 2.1
  • Updated the Wiki

v2.5.0.0

Sources

  • New: Added ValueTupleSource for ValueTuples
  • Changed: SmartObjects and SmartObjectsSource are depreciated in favor of ValueTupleSource

Settings

  • Breaking Change: Internal string comparisons (i.e. for placeholder names) are no more culture-specific, but Ordinal or OrdinalIgnoreCase respectively. See discussion under this issue.
  • Breaking Change: Default ErrorAction is now ThrowError for parser and formatter, instead of Ignore

Other

  • Changed: Removed all members which were flagged obsolete since more than a year.

v2.4.2.0

Fixed an issue with SmartObjects

v2.4.0.0 and v2.4.1.0

TimeFormatter

  • New: Supports DateTimeOffset as parameter
  • Changed in v2.4.1: DateTime operations always use their Universal Time representation. (Before, in case a DateTime had property Kind set to DateTimeKind.Unspecified, the result of a comparison was ambiguous.)
  • CTOR TimeFormatter(languageCode) throws for not implemented languageCode
  • CTOR TimeFormatter() is obsolete (redundant)
  • Obsolete in TimeSpanUtility
    • TimeSpan extension method Floor (redundant)
    • TimeSpan extension method Ceiling (redundant)

ConditionalFormatter

  • New: Supports DateTimeOffset as parameter
  • Changed: DateTime operations always use their Universal Time representation. (Before, in case a DateTime had property Kind set to Kind.Unspecified, the result of a comparison was ambiguous.)

Demo

  • Updated with DateTimeOffset example
  • Updated with TimeFormatter example

v2.3.1.0

  • Added SubStringFormatter thanks to arilani
  • Improved code coverage in unit tests
  • Updated dependencies
  • As announced: Dropped support for .NET 4.0, which was released back in 2010.

v2.3.0.0

  • Support for JSON Objects as data source
  • Added IsMatchFormatter thanks to ericpyle
  • Fixes issue with unsigned integers #101
  • This is the last version supporting .NET 4.0, which was released back in 2010.

v2.2.0.0

  • Fixes issue #101
  • This version includes a breaking change:
    • Before: OnParsingFailure event was invoked after each parsing error
    • Now: OnParsingFailure event is invoked after parsing is completed ParsingErrorEventArgs has a different signature. It now includes all ParsingErrors with all details that would be supplied during a parser exception. This also includes messages indicating where there the parse error occurred.

v2.1.0.2

  • Fixes issue #94

v2.1.0.1

v2.0.0

  • ReflectionSource now also gets members from base classes
  • Added nesting and list tests
  • Added coding samples
  • Improved source xml docs
  • Extended Wiki documentation for error handling and common pitfalls

v1.7.1.1

  • No more ambiguity between named formatters and string.Format formatting arguments: The parser checks whether the parsed name exists in one of the formatter extensions.
  • SmartFormatter and Parser default to ErrorAction = ErrorAction.Ignore in release AND debug mode
  • SmartFormatter has EventHandler OnFormattingFailure
  • Parser has EventHandler OnParsingFailure
  • Obsolete FormatItem.Text property removed (was replaced by RawText property some time ago)
  • Assemblies are signed with a strong name key file
  • Supported frameworks are .Net 4.0, .Net 4.5, .Net Core - dropped .Net 3.5 and earlier
  • Added an icon to the nuget package

v1.6.1

  • Fixed issue with parsing named formatter options (#45)
  • Minor changes to the Extensions API:
  • Added fields to ISelectorInfo
  • Made Text obsolete in favor of RawText.

v1.6

  • Added TemplateFormatter. Allows you to register named templates, to be used within other templates.
    Note: the TemplateFormatter extension is not a default extension, and must be added manually.

v1.5

  • Added "Nested Scopes" feature. This allows a nested template to easily access outer scopes, without tricky workarounds.
    For example: in "{Person: {Address: {City} {FirstName} } }", {City} will come from Address, but {FirstName} will come from Person.
    This will work especially well with conditional formatting!

v1.4

  • Massive improvements to the Extension API. Code is cleaner and easier to use.
  • Breaking changes: Any custom Extensions will need to be updated to match the new API.
    Long story short, instead of 5 parameters passed to IFormatter.EvaluateFormat, it now just gets a single FormatterInfo argument that contains all parameters.
  • Added the "choose" formatter, to eventually replace the ConditionalFormatter.
    Hopefully it's self explanatory with these usage examples:
    "{0:choose(1|2|3):one|two|three|default}" works like a switch statement "{0:choose(null):nothing|{something}}" can do null checking "{Gender:choose(Male|Female):his|her}" works great with Enums

v1.3

  • Added "Named Formatters", which allows you to use a specific formatter by specifying its name.
    For example, "{0:plural: ___ }" will use the Plural formatter, and "{0:default: ___ }" will use the default formatter.
  • Added "Formatter Options", which allows you to specify options for a named formatter. This will be used in the near future.
    For example, "{0:name(options): ___ }"

v1.2

  • Added .NET v3.5 and .NET v4.0 builds
  • Added "releases" folder to hold official releases
  • Added UseAlternativeBraces method, so that templates can use alternative characters
  • Added SmartFormatter.GetFormatterExtension and GetSourceExtension methods, which can be used to configure extensions
  • Added DefaultTwoLetterISOLanguageName properties to the TimeSpanFormatter and PluralLocalizationFormatter
  • Fixed AddExtensions to insert new extensions before existing ones (otherwise, it's pretty useless) (#17)

V1.1

  • #11 Added case insensitivity as option.
  • #12 Added support for expando objects.

V1.0

Converted from "CustomFormat" (VB.NET)