├── .gitignore ├── LICENSE ├── LICENSE_NodaTime ├── LICENSE_ServiceStack ├── NodaTimeLogo.png ├── README.md ├── build.bat ├── build.cake ├── build.ps1 ├── cake.config ├── src ├── Directory.Build.NugetPackage.props ├── Directory.Build.SourceLink.props ├── Directory.Build.Version.props ├── Directory.Build.props ├── NodaTime.Serialization.ServiceStackText.UnitTests │ ├── ComplexJsonIntervalSerializerTests.cs │ ├── DateTimeZoneSerializerTests.cs │ ├── DefaultNodaSerializerSettingsTests.cs │ ├── ExtendedIsoIntervalSerializerTests.cs │ ├── ExtensionsTests.cs │ ├── NodaSerializerDefinitionsTests.cs │ ├── NodaTime.Serialization.ServiceStackText.UnitTests.csproj │ ├── ServiceStackFallbackDeserializersTests.cs │ └── StandardServiceStackSerializerTest.cs ├── NodaTime.Serialization.ServiceStackText.sln └── NodaTime.Serialization.ServiceStackText │ ├── ComplexJsonIntervalSerializer.cs │ ├── ComplexRawIntervalDto.cs │ ├── DateTimeZoneSerializer.cs │ ├── DefaultNodaSerializerSettings.cs │ ├── ExtendedIsoIntervalSerializer.cs │ ├── Extensions.cs │ ├── INodaSerializerSettings.cs │ ├── IServiceStackSerializer.cs │ ├── JsConfigWrapper.cs │ ├── NodaSerializerDefinitions.cs │ ├── NodaTime.Serialization.ServiceStackText.csproj │ ├── ServiceStackFallbackDeserializers.cs │ └── StandardServiceStackSerializer.cs └── version.json /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | 244 | # SQL Server files 245 | *.mdf 246 | *.ldf 247 | *.ndf 248 | 249 | # Business Intelligence projects 250 | *.rdl.data 251 | *.bim.layout 252 | *.bim_*.settings 253 | *.rptproj.rsuser 254 | 255 | # Microsoft Fakes 256 | FakesAssemblies/ 257 | 258 | # GhostDoc plugin setting file 259 | *.GhostDoc.xml 260 | 261 | # Node.js Tools for Visual Studio 262 | .ntvs_analysis.dat 263 | node_modules/ 264 | 265 | # Visual Studio 6 build log 266 | *.plg 267 | 268 | # Visual Studio 6 workspace options file 269 | *.opt 270 | 271 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 272 | *.vbw 273 | 274 | # Visual Studio LightSwitch build output 275 | **/*.HTMLClient/GeneratedArtifacts 276 | **/*.DesktopClient/GeneratedArtifacts 277 | **/*.DesktopClient/ModelManifest.xml 278 | **/*.Server/GeneratedArtifacts 279 | **/*.Server/ModelManifest.xml 280 | _Pvt_Extensions 281 | 282 | # Paket dependency manager 283 | .paket/paket.exe 284 | paket-files/ 285 | 286 | # FAKE - F# Make 287 | .fake/ 288 | 289 | # JetBrains Rider 290 | .idea/ 291 | *.sln.iml 292 | 293 | # CodeRush 294 | .cr/ 295 | 296 | # Python Tools for Visual Studio (PTVS) 297 | __pycache__/ 298 | *.pyc 299 | 300 | # Cake - Uncomment if you are using it 301 | # tools/** 302 | # !tools/packages.config 303 | 304 | # Tabs Studio 305 | *.tss 306 | 307 | # Telerik's JustMock configuration file 308 | *.jmconfig 309 | 310 | # BizTalk build output 311 | *.btp.cs 312 | *.btm.cs 313 | *.odx.cs 314 | *.xsd.cs 315 | 316 | # OpenCover UI analysis results 317 | OpenCover/ 318 | 319 | # Azure Stream Analytics local run output 320 | ASALocalRun/ 321 | 322 | # MSBuild Binary and Structured Log 323 | *.binlog 324 | 325 | # NVidia Nsight GPU debugger configuration file 326 | *.nvuser 327 | 328 | # MFractors (Xamarin productivity tool) working folder 329 | .mfractor/ 330 | /dotcover 331 | /ReleasePackages 332 | /tools 333 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019, Anthony Carl 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /LICENSE_NodaTime: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /LICENSE_ServiceStack: -------------------------------------------------------------------------------- 1 | ServiceStack 2 | Copyright (c) 2013 ServiceStack 3 | =============================================================================== 4 | 5 | This program is free software: you can redistribute it and/or modify it 6 | under the terms of the GNU Affero General Public License as published by the 7 | Free Software Foundation, either version 3 of the License, see 8 | http://www.gnu.org/licenses/agpl-3.0.html. 9 | 10 | This program is distributed in the hope that it will be useful, but WITHOUT 11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 12 | FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 13 | 14 | 15 | FOSS License Exception 16 | =============================================================================== 17 | 18 | This Exception applies to open source applications built with ServiceStack and 19 | ServiceStack extensions ("The Software"), and to open source Derivative Works of 20 | the Software, that use the Software under the terms of GNU Affero General 21 | Public License, version 3 ("AGPLv3"). The Exception extends AGPLv3 by providing 22 | additional grants that allows developers of FOSS applications to include ServiceStack 23 | with their FOSS applications in combination with other software licensed under 24 | the licenses from the "Open Source License List" below, provided that: 25 | 26 | You obey the AGPLv3 terms for the Software and the Derivative Work, except for 27 | the separate parts of the Derivative Work ("Additions") which constitute independent 28 | work and are not dervied from the Software. 29 | 30 | - All Additions are distributed subject to one of the licenses listed below. 31 | - Your software distribution provides complete source code for the Additions. 32 | - The Derivative Work and its Additions are intended for use in end-user applications 33 | and do not constitute software intended for use by software developers, such as 34 | software libraries, components, and development kits. 35 | - If you violate any of the terms in this Exception, you lose all rights granted 36 | to you by the Exception and revert to the terms of AGPLv3. 37 | 38 | Service Stack reserves all rights not expressly granted in these terms and conditions. 39 | 40 | Open Source License List 41 | 42 | Name Version 43 | Academic Free License 2.0 44 | Apache Software License 2.0 45 | Apple Public Source License 2.0 46 | Artistic license From Perl 5.8.0 47 | BSD license July 22 1999 48 | Common Development and Distribution License (CDDL) 1.0 49 | Common Public License 1.0 50 | Eclipse Public License 1.0 51 | Educational Community License 2.0 52 | European Union Public License (EUPL) 1.1 53 | GNU General Public License (GPL) 2.0 54 | GNU Library or "Lesser" General Public License (LGPL) 3.0 55 | Jabber Open Source License 1.0 56 | MIT License (As listed in file MIT-License.txt) - 57 | Mozilla Public License (MPL) 1.0/1.1 58 | Open Software License 2.0 59 | OpenSSL license (with original SSLeay license) 2003 (1998) 60 | University of Illinois/NCSA Open Source License - 61 | W3C License 2001 62 | X11 License 2001 63 | Zlib/libpng License - 64 | 65 | 66 | 67 | Commercial License 68 | =========================================================================== 69 | In addition to this license, ServiceStack is offered under a commerical license. 70 | Contact team@servicestack.net for details. -------------------------------------------------------------------------------- /NodaTimeLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AnthonyCarl/NodaTime.Serialization.ServiceStackText/9f04793dcdbfc64a36dbc7c8a6e0f72ccb37da6a/NodaTimeLogo.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NodaTime.Serialization.ServiceStackText 2 | ======================================= 3 | 4 | [`ServiceStack.Text`](https://github.com/ServiceStack/ServiceStack.Text) JSON serialization support for [`NodaTime v2`](http://nodatime.org/). This library will work with `v5.0` and later versions of `ServiceStack.Text`. 5 | 6 | ## Typical Setup 7 | ```cs 8 | DateTimeZoneProviders.Tzdb 9 | .CreateDefaultSerializersForNodaTime() 10 | .ConfigureSerializersForNodaTime(); 11 | ``` 12 | 13 | ## Optional Setup 14 | There are static extension methods to allow for optional fluent configuration. 15 | 16 | ### Use Iso Interval Serializer 17 | ```cs 18 | DateTimeZoneProviders.Tzdb.CreateDefaultSerializersForNodaTime() 19 | .WithIsoIntervalSerializer() 20 | .ConfigureSerializersForNodaTime(); 21 | ``` 22 | 23 | ### Use Iso Period Serializer 24 | ```cs 25 | DateTimeZoneProviders.Tzdb.CreateDefaultSerializersForNodaTime() 26 | .WithNormalizingIsoPeriodSerializer() 27 | .ConfigureSerializersForNodaTime(); 28 | ``` 29 | 30 | ### One-Off Serializer Setup 31 | ```cs 32 | //You could create you own serializer by implementing IServiceStackSerializer 33 | NodaSerializerDefinitions.LocalTimeSerializer.ConfigureSerializer(); 34 | ``` 35 | 36 | Alternatively, you can create your own implementation of [`INodaSerializerSettings`](https://github.com/AnthonyCarl/NodaTime.Serialization.ServiceStackText/blob/master/src/NodaTime.Serialization.ServiceStackText/INodaSerializerSettings.cs) to suit your specific needs. 37 | 38 | ## Default Serializers 39 | The `SerivceStack.Text` serializer uses the same default serialization formats as those used in the [`Json.NET` serializers](http://nodatime.org/1.2.x/userguide/serialization.html). You can optionally change the `Interval` serializer to use the [`ISO 8601` Interval spec](http://en.wikipedia.org/wiki/ISO_8601#Time_intervals). 40 | 41 | 42 | 43 | ## Notes 44 | - Serializer setup should only occur once in your application root. 45 | - Serialization setup should occur before any serialization occurs with `ServiceStack.Text` or there may be undesirable behavior. 46 | - If using with ServiceStack, serializer setup should occur before Init of the AppHost. 47 | - For Value types, this will also setup the nullable serializer for that value type. 48 | - Since this is a custom serializer for `ServiceStack.Text`, calls to `JsConfig.Reset()` will remove the custom serializers. 49 | 50 | ## Using the Code 51 | 52 | * [Install the NuGet Package](https://nuget.org/packages/NodaTime.Serialization.ServiceStackText) 53 | * [NuGet Packages from Latest Successful Build](https://teamcity.jetbrains.com/viewLog.html?buildId=lastSuccessful&buildTypeId=bt1209&tab=artifacts) 54 | * You can check out the code and run `build.bat`. It will create NuGet packages you can consume in `.\ReleasePackages` or you can directly use the resulting binaries. 55 | 56 | ## Continuous Integration 57 | [JetBrains TeamCity CI Build Status:](https://teamcity.jetbrains.com/viewType.html?buildTypeId=bt1210&guest=1) ![Build Status](http://teamcity.codebetter.com/app/rest/builds/buildType:(id:bt1209)/statusIcon) 58 | 59 | Special Thanks to [JetBrains](http://www.jetbrains.com/teamcity) for hosting this project! 60 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | powershell .\build.ps1 2 | -------------------------------------------------------------------------------- /build.cake: -------------------------------------------------------------------------------- 1 | #tool "nuget:?package=JetBrains.dotCover.CommandLineTools&version=2019.2.3" 2 | #tool "nuget:?package=xunit.runner.console&version=2.4.1" 3 | #addin nuget:?package=Cake.GitVersioning&version=3.0.28 4 | 5 | var target = Argument("target", "Default"); 6 | var buildConfiguration = Argument("configuration", "Release"); 7 | 8 | Task("Default") 9 | .IsDependentOn("Pack") 10 | .Does(() => 11 | { 12 | }); 13 | 14 | Task("SetVersion") 15 | .Does(() => 16 | { 17 | TeamCity.SetBuildNumber(GitVersioningGetVersion().SemVer2); 18 | }); 19 | 20 | Task("Restore") 21 | .IsDependentOn("SetVersion") 22 | .Does(() => 23 | { 24 | var restoreSettings = new DotNetCoreRestoreSettings 25 | { 26 | Sources = new[] {"https://api.nuget.org/v3/index.json"} 27 | }; 28 | DotNetCoreRestore("./src/NodaTime.Serialization.ServiceStackText.sln", restoreSettings); 29 | }); 30 | 31 | Task("Build") 32 | .IsDependentOn("Restore") 33 | .Does(() => 34 | { 35 | var buildSettings = new DotNetCoreBuildSettings 36 | { 37 | Configuration = buildConfiguration 38 | }; 39 | DotNetCoreBuild("./src/NodaTime.Serialization.ServiceStackText.sln", buildSettings); 40 | }); 41 | 42 | Task("Test") 43 | .IsDependentOn("Build") 44 | .Does(() => 45 | { 46 | Action runTests = (ctx, framework) => { 47 | ctx.DotNetCoreTest("./src/NodaTime.Serialization.ServiceStackText.UnitTests/NodaTime.Serialization.ServiceStackText.UnitTests.csproj", new DotNetCoreTestSettings 48 | { 49 | Configuration = buildConfiguration, 50 | Framework = framework, 51 | NoBuild = true 52 | }); 53 | }; 54 | 55 | var coverSettings = new DotCoverCoverSettings() 56 | .WithFilter("-:xunit*") 57 | .WithFilter("-:NuGet*") 58 | .WithFilter("-:MSBuild*") 59 | .WithFilter("-:*Tests") 60 | .WithFilter("-:ServiceStack*") 61 | .WithFilter("-:NodaTime") 62 | .WithFilter("-:NodaTime.Testing"); 63 | 64 | var coverageResult452 = new FilePath("./dotcover/dotcover452.data"); 65 | DotCoverCover( 66 | ctx => runTests(ctx, "net452"), 67 | coverageResult452, 68 | coverSettings); 69 | 70 | var coverageResultCoreApp20 = new FilePath("./dotcover/dotcoverCoreApp20.data"); 71 | DotCoverCover( 72 | ctx => runTests(ctx, "netcoreapp2.0"), 73 | coverageResultCoreApp20, 74 | coverSettings); 75 | 76 | var mergedData = new FilePath("./dotcover/dotcoverMerged.data"); 77 | DotCoverMerge( 78 | new []{ 79 | coverageResult452, 80 | coverageResultCoreApp20 81 | }, 82 | mergedData, 83 | new DotCoverMergeSettings()); 84 | 85 | if(TeamCity.IsRunningOnTeamCity) { 86 | TeamCity.ImportDotCoverCoverage( 87 | mergedData, 88 | MakeAbsolute(Directory("./tools/JetBrains.dotCover.CommandLineTools.2019.2.3/tools")) 89 | ); 90 | } 91 | else { 92 | var htmlReportFile = new FilePath("./dotcover/dotcover.html"); 93 | var reportSettings = new DotCoverReportSettings { ReportType = DotCoverReportType.HTML}; 94 | DotCoverReport(mergedData, htmlReportFile, reportSettings); 95 | StartProcess("powershell", "start file:///" + MakeAbsolute(htmlReportFile)); 96 | } 97 | }); 98 | 99 | Task("Pack") 100 | .IsDependentOn("Test") 101 | .Does(() => 102 | { 103 | var packSettings = new DotNetCorePackSettings 104 | { 105 | Configuration = buildConfiguration, 106 | OutputDirectory = "./ReleasePackages/", 107 | ArgumentCustomization = args => args.Append("--include-symbols") 108 | .Append("--include-source") 109 | }; 110 | DotNetCorePack("./src/NodaTime.Serialization.ServiceStackText/NodaTime.Serialization.ServiceStackText.csproj", packSettings); 111 | }); 112 | 113 | RunTarget(target); 114 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # This is the Cake bootstrapper script for PowerShell. 3 | # This file was downloaded from https://github.com/cake-build/resources 4 | # Feel free to change this file to fit your needs. 5 | ########################################################################## 6 | 7 | <# 8 | 9 | .SYNOPSIS 10 | This is a Powershell script to bootstrap a Cake build. 11 | 12 | .DESCRIPTION 13 | This Powershell script will download NuGet if missing, restore NuGet tools (including Cake) 14 | and execute your Cake build script with the parameters you provide. 15 | 16 | .PARAMETER Script 17 | The build script to execute. 18 | .PARAMETER Target 19 | The build script target to run. 20 | .PARAMETER Configuration 21 | The build configuration to use. 22 | .PARAMETER Verbosity 23 | Specifies the amount of information to be displayed. 24 | .PARAMETER ShowDescription 25 | Shows description about tasks. 26 | .PARAMETER DryRun 27 | Performs a dry run. 28 | .PARAMETER SkipToolPackageRestore 29 | Skips restoring of packages. 30 | .PARAMETER ScriptArgs 31 | Remaining arguments are added here. 32 | 33 | .LINK 34 | https://cakebuild.net 35 | 36 | #> 37 | 38 | [CmdletBinding()] 39 | Param( 40 | [string]$Script = "build.cake", 41 | [string]$Target, 42 | [string]$Configuration, 43 | [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] 44 | [string]$Verbosity, 45 | [switch]$ShowDescription, 46 | [Alias("WhatIf", "Noop")] 47 | [switch]$DryRun, 48 | [switch]$SkipToolPackageRestore, 49 | [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] 50 | [string[]]$ScriptArgs 51 | ) 52 | 53 | # Attempt to set highest encryption available for SecurityProtocol. 54 | # PowerShell will not set this by default (until maybe .NET 4.6.x). This 55 | # will typically produce a message for PowerShell v2 (just an info 56 | # message though) 57 | try { 58 | # Set TLS 1.2 (3072), then TLS 1.1 (768), then TLS 1.0 (192), finally SSL 3.0 (48) 59 | # Use integers because the enumeration values for TLS 1.2 and TLS 1.1 won't 60 | # exist in .NET 4.0, even though they are addressable if .NET 4.5+ is 61 | # installed (.NET 4.5 is an in-place upgrade). 62 | # PowerShell Core already has support for TLS 1.2 so we can skip this if running in that. 63 | if (-not $IsCoreCLR) { 64 | [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 -bor 48 65 | } 66 | } catch { 67 | Write-Output 'Unable to set PowerShell to use TLS 1.2 and TLS 1.1 due to old .NET Framework installed. If you see underlying connection closed or trust errors, you may need to upgrade to .NET Framework 4.5+ and PowerShell v3' 68 | } 69 | 70 | [Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null 71 | function MD5HashFile([string] $filePath) 72 | { 73 | if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf)) 74 | { 75 | return $null 76 | } 77 | 78 | [System.IO.Stream] $file = $null; 79 | [System.Security.Cryptography.MD5] $md5 = $null; 80 | try 81 | { 82 | $md5 = [System.Security.Cryptography.MD5]::Create() 83 | $file = [System.IO.File]::OpenRead($filePath) 84 | return [System.BitConverter]::ToString($md5.ComputeHash($file)) 85 | } 86 | finally 87 | { 88 | if ($file -ne $null) 89 | { 90 | $file.Dispose() 91 | } 92 | } 93 | } 94 | 95 | function GetProxyEnabledWebClient 96 | { 97 | $wc = New-Object System.Net.WebClient 98 | $proxy = [System.Net.WebRequest]::GetSystemWebProxy() 99 | $proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials 100 | $wc.Proxy = $proxy 101 | return $wc 102 | } 103 | 104 | Write-Host "Preparing to run build script..." 105 | 106 | if(!$PSScriptRoot){ 107 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent 108 | } 109 | 110 | $TOOLS_DIR = Join-Path $PSScriptRoot "tools" 111 | $ADDINS_DIR = Join-Path $TOOLS_DIR "Addins" 112 | $MODULES_DIR = Join-Path $TOOLS_DIR "Modules" 113 | $NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" 114 | $CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe" 115 | $NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" 116 | $PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config" 117 | $PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum" 118 | $ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config" 119 | $MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config" 120 | 121 | # Make sure tools folder exists 122 | if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { 123 | Write-Verbose -Message "Creating tools directory..." 124 | New-Item -Path $TOOLS_DIR -Type Directory | Out-Null 125 | } 126 | 127 | # Make sure that packages.config exist. 128 | if (!(Test-Path $PACKAGES_CONFIG)) { 129 | Write-Verbose -Message "Downloading packages.config..." 130 | try { 131 | $wc = GetProxyEnabledWebClient 132 | $wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) 133 | } catch { 134 | Throw "Could not download packages.config." 135 | } 136 | } 137 | 138 | # Try find NuGet.exe in path if not exists 139 | if (!(Test-Path $NUGET_EXE)) { 140 | Write-Verbose -Message "Trying to find nuget.exe in PATH..." 141 | $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) } 142 | $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1 143 | if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) { 144 | Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)." 145 | $NUGET_EXE = $NUGET_EXE_IN_PATH.FullName 146 | } 147 | } 148 | 149 | # Try download NuGet.exe if not exists 150 | if (!(Test-Path $NUGET_EXE)) { 151 | Write-Verbose -Message "Downloading NuGet.exe..." 152 | try { 153 | $wc = GetProxyEnabledWebClient 154 | $wc.DownloadFile($NUGET_URL, $NUGET_EXE) 155 | } catch { 156 | Throw "Could not download NuGet.exe." 157 | } 158 | } 159 | 160 | # Save nuget.exe path to environment to be available to child processed 161 | $env:NUGET_EXE = $NUGET_EXE 162 | $env:NUGET_EXE_INVOCATION = if ($IsLinux -or $IsMacOS) { 163 | "mono `"$NUGET_EXE`"" 164 | } else { 165 | "`"$NUGET_EXE`"" 166 | } 167 | 168 | # Restore tools from NuGet? 169 | if(-Not $SkipToolPackageRestore.IsPresent) { 170 | Push-Location 171 | Set-Location $TOOLS_DIR 172 | 173 | # Check for changes in packages.config and remove installed tools if true. 174 | [string] $md5Hash = MD5HashFile $PACKAGES_CONFIG 175 | if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or 176 | ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) { 177 | Write-Verbose -Message "Missing or changed package.config hash..." 178 | Get-ChildItem -Exclude packages.config,nuget.exe,Cake.Bakery | 179 | Remove-Item -Recurse -Force 180 | } 181 | 182 | Write-Verbose -Message "Restoring tools from NuGet..." 183 | 184 | $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`"" 185 | 186 | if ($LASTEXITCODE -ne 0) { 187 | Throw "An error occurred while restoring NuGet tools." 188 | } 189 | else 190 | { 191 | $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII" 192 | } 193 | Write-Verbose -Message ($NuGetOutput | Out-String) 194 | 195 | Pop-Location 196 | } 197 | 198 | # Restore addins from NuGet 199 | if (Test-Path $ADDINS_PACKAGES_CONFIG) { 200 | Push-Location 201 | Set-Location $ADDINS_DIR 202 | 203 | Write-Verbose -Message "Restoring addins from NuGet..." 204 | $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`"" 205 | 206 | if ($LASTEXITCODE -ne 0) { 207 | Throw "An error occurred while restoring NuGet addins." 208 | } 209 | 210 | Write-Verbose -Message ($NuGetOutput | Out-String) 211 | 212 | Pop-Location 213 | } 214 | 215 | # Restore modules from NuGet 216 | if (Test-Path $MODULES_PACKAGES_CONFIG) { 217 | Push-Location 218 | Set-Location $MODULES_DIR 219 | 220 | Write-Verbose -Message "Restoring modules from NuGet..." 221 | $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`"" 222 | 223 | if ($LASTEXITCODE -ne 0) { 224 | Throw "An error occurred while restoring NuGet modules." 225 | } 226 | 227 | Write-Verbose -Message ($NuGetOutput | Out-String) 228 | 229 | Pop-Location 230 | } 231 | 232 | # Make sure that Cake has been installed. 233 | if (!(Test-Path $CAKE_EXE)) { 234 | Throw "Could not find Cake.exe at $CAKE_EXE" 235 | } 236 | 237 | $CAKE_EXE_INVOCATION = if ($IsLinux -or $IsMacOS) { 238 | "mono `"$CAKE_EXE`"" 239 | } else { 240 | "`"$CAKE_EXE`"" 241 | } 242 | 243 | # Build an array (not a string) of Cake arguments to be joined later 244 | $cakeArguments = @() 245 | if ($Script) { $cakeArguments += "`"$Script`"" } 246 | if ($Target) { $cakeArguments += "-target=`"$Target`"" } 247 | if ($Configuration) { $cakeArguments += "-configuration=$Configuration" } 248 | if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" } 249 | if ($ShowDescription) { $cakeArguments += "-showdescription" } 250 | if ($DryRun) { $cakeArguments += "-dryrun" } 251 | $cakeArguments += $ScriptArgs 252 | 253 | # Start Cake 254 | Write-Host "Running build script..." 255 | Invoke-Expression "& $CAKE_EXE_INVOCATION $($cakeArguments -join " ")" 256 | exit $LASTEXITCODE 257 | -------------------------------------------------------------------------------- /cake.config: -------------------------------------------------------------------------------- 1 | [Nuget] 2 | Source=https://api.nuget.org/v3/index.json 3 | -------------------------------------------------------------------------------- /src/Directory.Build.NugetPackage.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | Anthony Carl 4 | Anthony Carl 5 | Copyright 2019 Anthony Carl 6 | false 7 | https://github.com/AnthonyCarl/NodaTime.Serialization.ServiceStackText 8 | https://github.com/AnthonyCarl/NodaTime.Serialization.ServiceStackText 9 | 10 | NodaTime Serializer ServiceStack ServiceStack.Text JSON Serialize date time timezone calendar 11 | ServiceStack.Text JSON serializer support for NodaTime 12 | ServiceStack.Text support for NodaTime 13 | 14 | Added support for AnnualDate. 15 | Remove support for .Net Standard 1.3 16 | 17 | BSD-2-Clause 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Directory.Build.SourceLink.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb 5 | 6 | true 7 | false 8 | true 9 | true 10 | snupkg 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Directory.Build.Version.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 3.0.28 5 | all 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | latest 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText.UnitTests/ComplexJsonIntervalSerializerTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NodaTime.Utility; 3 | using Xunit; 4 | 5 | namespace NodaTime.Serialization.ServiceStackText.UnitTests 6 | { 7 | public class ComplexJsonIntervalSerializerTests 8 | { 9 | [Fact] 10 | public void Serialize() 11 | { 12 | var startInstant = Instant.FromUtc(2012, 1, 2, 3, 4, 5); 13 | var endInstant = Instant.FromUtc(2013, 6, 7, 8, 9, 10); 14 | var interval = new Interval(startInstant, endInstant); 15 | 16 | var json = NodaSerializerDefinitions.ComplexIntervalSerializer.Serialize(interval); 17 | 18 | string expectedJson = "{\"Start\":\"2012-01-02T03:04:05Z\",\"End\":\"2013-06-07T08:09:10Z\"}"; 19 | Assert.Equal(expectedJson, json); 20 | } 21 | 22 | [Fact] 23 | public void Deserialize() 24 | { 25 | string json = "{\"Start\":\"2012-01-02T03:04:05Z\",\"End\":\"2013-06-07T08:09:10Z\"}"; 26 | 27 | var interval = NodaSerializerDefinitions.ComplexIntervalSerializer.Deserialize(json); 28 | 29 | var startInstant = Instant.FromUtc(2012, 1, 2, 3, 4, 5); 30 | var endInstant = Instant.FromUtc(2013, 6, 7, 8, 9, 10); 31 | var expectedInterval = new Interval(startInstant, endInstant); 32 | Assert.Equal(expectedInterval, interval); 33 | } 34 | 35 | [Fact] 36 | public void Deserialize_MissingEnd_Throws() 37 | { 38 | string json = "{\"Start\":\"2012-01-02T03:04:05Z\"}"; 39 | Assert.Throws( 40 | () => NodaSerializerDefinitions.ComplexIntervalSerializer.Deserialize(json)); 41 | } 42 | 43 | [Fact] 44 | public void Deserialize_MissingStart_Throws() 45 | { 46 | string json = "{\"End\":\"2012-01-02T03:04:05Z\"}"; 47 | 48 | Assert.Throws( 49 | () => NodaSerializerDefinitions.ComplexIntervalSerializer.Deserialize(json)); 50 | } 51 | 52 | [Fact] 53 | public void UseRawSerializer_Default_True() 54 | { 55 | Assert.True(NodaSerializerDefinitions.ComplexIntervalSerializer.UseRawSerializer); 56 | } 57 | 58 | [Fact] 59 | public void Constructor_NullInstantSerializer_Throws() 60 | { 61 | Assert.Throws(() => new ComplexJsonIntervalSerializer(null)); 62 | } 63 | 64 | [Fact] 65 | public void Deserialize_NullText_Throws() 66 | { 67 | Assert.Throws( 68 | () => NodaSerializerDefinitions.ComplexIntervalSerializer.Deserialize(null)); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText.UnitTests/DateTimeZoneSerializerTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NodaTime.TimeZones; 3 | using Xunit; 4 | 5 | namespace NodaTime.Serialization.ServiceStackText.UnitTests 6 | { 7 | public class DateTimeZoneSerializerTests 8 | { 9 | private readonly IServiceStackSerializer serializer = NodaSerializerDefinitions.CreateDateTimeZoneSerializer(DateTimeZoneProviders.Tzdb); 10 | 11 | [Fact] 12 | public void Serialize() 13 | { 14 | var dateTimeZone = DateTimeZoneProviders.Tzdb["America/Los_Angeles"]; 15 | var json = serializer.Serialize(dateTimeZone); 16 | string expectedJson = "America/Los_Angeles"; 17 | Assert.Equal(expectedJson, json); 18 | } 19 | 20 | [Fact] 21 | public void Deserialize() 22 | { 23 | string json = "America/Los_Angeles"; 24 | var dateTimeZone = serializer.Deserialize(json); 25 | var expectedDateTimeZone = DateTimeZoneProviders.Tzdb["America/Los_Angeles"]; 26 | Assert.Equal(expectedDateTimeZone, dateTimeZone); 27 | } 28 | 29 | [Fact] 30 | public void Deserialize_TimeZoneNotFound() 31 | { 32 | string json = "America/DOES_NOT_EXIST"; 33 | Assert.Throws(() => serializer.Deserialize(json)); 34 | } 35 | 36 | [Fact] 37 | public void Constructor_NullProvider_Throws() 38 | { 39 | Assert.Throws(() => new DateTimeZoneSerializer(null)); 40 | } 41 | 42 | [Fact] 43 | public void Serialize_NullDateTimeZone_NullString() 44 | { 45 | var serializer = NodaSerializerDefinitions.CreateDateTimeZoneSerializer(DateTimeZoneProviders.Tzdb); 46 | Assert.Null(serializer.Serialize(null)); 47 | } 48 | 49 | [Fact] 50 | public void Deserialize_WrongCaseForId_ReturnsCorrectDateTimeZone() 51 | { 52 | var serializer = NodaSerializerDefinitions.CreateDateTimeZoneSerializer(DateTimeZoneProviders.Tzdb); 53 | var expectedTimeZone = DateTimeZoneProviders.Tzdb["America/Los_Angeles"]; 54 | var actualTimeZone = serializer.Deserialize("amerICa/LOS_angELes"); 55 | Assert.Equal(expectedTimeZone,actualTimeZone); 56 | } 57 | 58 | [Fact] 59 | public void Deserialize_EmptyId_Throws() 60 | { 61 | var serializer = NodaSerializerDefinitions.CreateDateTimeZoneSerializer(DateTimeZoneProviders.Tzdb); 62 | Assert.Throws(() => serializer.Deserialize(string.Empty)); 63 | } 64 | 65 | [Fact] 66 | public void UseRawSerializer_Default_False() 67 | { 68 | var serializer = NodaSerializerDefinitions.CreateDateTimeZoneSerializer(DateTimeZoneProviders.Tzdb); 69 | Assert.False(serializer.UseRawSerializer); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText.UnitTests/DefaultNodaSerializerSettingsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | 4 | namespace NodaTime.Serialization.ServiceStackText.UnitTests 5 | { 6 | public class DefaultNodaSerializerSettingsTests 7 | { 8 | [Fact] 9 | public void Constructor_NullDateTimeZoneProvider_Throws() 10 | { 11 | Assert.Throws(() => new DefaultNodaSerializerSettings(null)); 12 | } 13 | 14 | [Fact] 15 | public void DurationSerializer_Default_Verify() 16 | { 17 | var serializerSettings = new DefaultNodaSerializerSettings(DateTimeZoneProviders.Tzdb); 18 | Assert.Same(NodaSerializerDefinitions.DurationSerializer, serializerSettings.DurationSerializer); 19 | } 20 | 21 | [Fact] 22 | public void InstantSerializer_Default_Verify() 23 | { 24 | var serializerSettings = new DefaultNodaSerializerSettings(DateTimeZoneProviders.Tzdb); 25 | Assert.Same(NodaSerializerDefinitions.InstantSerializer, serializerSettings.InstantSerializer); 26 | } 27 | 28 | [Fact] 29 | public void IntervalSerializer_Default_Verify() 30 | { 31 | var serializerSettings = new DefaultNodaSerializerSettings(DateTimeZoneProviders.Tzdb); 32 | Assert.Same(NodaSerializerDefinitions.ComplexIntervalSerializer, serializerSettings.IntervalSerializer); 33 | } 34 | 35 | [Fact] 36 | public void LocalDateSerializer_Default_Verify() 37 | { 38 | var serializerSettings = new DefaultNodaSerializerSettings(DateTimeZoneProviders.Tzdb); 39 | Assert.Same(NodaSerializerDefinitions.LocalDateSerializer, serializerSettings.LocalDateSerializer); 40 | } 41 | 42 | [Fact] 43 | public void LocalDateTimeSerializer_Default_Verify() 44 | { 45 | var serializerSettings = new DefaultNodaSerializerSettings(DateTimeZoneProviders.Tzdb); 46 | Assert.Same(NodaSerializerDefinitions.LocalDateTimeSerializer, serializerSettings.LocalDateTimeSerializer); 47 | } 48 | 49 | [Fact] 50 | public void LocalTimeSerializer_Default_Verify() 51 | { 52 | var serializerSettings = new DefaultNodaSerializerSettings(DateTimeZoneProviders.Tzdb); 53 | Assert.Same(NodaSerializerDefinitions.LocalTimeSerializer, serializerSettings.LocalTimeSerializer); 54 | } 55 | 56 | [Fact] 57 | public void OffsetSerializer_Default_Verify() 58 | { 59 | var serializerSettings = new DefaultNodaSerializerSettings(DateTimeZoneProviders.Tzdb); 60 | Assert.Same(NodaSerializerDefinitions.OffsetSerializer, serializerSettings.OffsetSerializer); 61 | } 62 | 63 | [Fact] 64 | public void OffsetDateTimeSerializer_Default_Verify() 65 | { 66 | var serializerSettings = new DefaultNodaSerializerSettings(DateTimeZoneProviders.Tzdb); 67 | Assert.Same(NodaSerializerDefinitions.OffsetDateTimeSerializer, serializerSettings.OffsetDateTimeSerializer); 68 | } 69 | 70 | [Fact] 71 | public void PeriodSerializer_Default_Verify() 72 | { 73 | var serializerSettings = new DefaultNodaSerializerSettings(DateTimeZoneProviders.Tzdb); 74 | Assert.Same(NodaSerializerDefinitions.RoundtripPeriodSerializer, serializerSettings.PeriodSerializer); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText.UnitTests/ExtendedIsoIntervalSerializerTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NodaTime.Text; 3 | using NodaTime.Utility; 4 | using Xunit; 5 | 6 | namespace NodaTime.Serialization.ServiceStackText.UnitTests 7 | { 8 | public class ExtendedIsoIntervalSerializerTests 9 | { 10 | [Fact] 11 | public void Serialize() 12 | { 13 | var startInstant = Instant.FromUtc(2012, 1, 2, 3, 4, 5); 14 | var endInstant = Instant.FromUtc(2013, 6, 7, 8, 9, 10); 15 | var interval = new Interval(startInstant, endInstant); 16 | 17 | var json = NodaSerializerDefinitions.ExtendedIsoIntervalSerializer.Serialize(interval); 18 | 19 | string expectedJson = "2012-01-02T03:04:05Z/2013-06-07T08:09:10Z"; 20 | Assert.Equal(expectedJson, json); 21 | } 22 | 23 | [Fact] 24 | public void Deserialize() 25 | { 26 | string json = "2012-01-02T03:04:05Z/2013-06-07T08:09:10Z"; 27 | 28 | var interval = NodaSerializerDefinitions.ExtendedIsoIntervalSerializer.Deserialize(json); 29 | 30 | var startInstant = Instant.FromUtc(2012, 1, 2, 3, 4, 5); 31 | var endInstant = Instant.FromUtc(2013, 6, 7, 8, 9, 10); 32 | var expectedInterval = new Interval(startInstant, endInstant); 33 | Assert.Equal(expectedInterval, interval); 34 | } 35 | 36 | [Fact] 37 | public void Deserialize_MissingEnd_Throws() 38 | { 39 | string json = "2012-01-02T03:04:05Z/"; 40 | Assert.Throws( 41 | () => NodaSerializerDefinitions.ExtendedIsoIntervalSerializer.Deserialize(json)); 42 | } 43 | 44 | [Fact] 45 | public void Deserialize_MissingStart_Throws() 46 | { 47 | string json = "/2012-01-02T03:04:05Z"; 48 | 49 | Assert.Throws( 50 | () => NodaSerializerDefinitions.ExtendedIsoIntervalSerializer.Deserialize(json)); 51 | } 52 | 53 | [Fact] 54 | public void Deserialize_EmptyString_Throws() 55 | { 56 | Assert.Throws( 57 | () => NodaSerializerDefinitions.ExtendedIsoIntervalSerializer.Deserialize(string.Empty)); 58 | } 59 | 60 | [Fact] 61 | public void Deserialize_NoSlash_Throws() 62 | { 63 | Assert.Throws( 64 | () => NodaSerializerDefinitions.ExtendedIsoIntervalSerializer.Deserialize("NotGoingToWork")); 65 | } 66 | 67 | [Fact] 68 | public void UseRawSerializer_Default_False() 69 | { 70 | Assert.False(NodaSerializerDefinitions.ExtendedIsoIntervalSerializer.UseRawSerializer); 71 | } 72 | 73 | [Fact] 74 | public void Constructor_NullInstantSerializer_Throws() 75 | { 76 | Assert.Throws(() => new ExtendedIsoIntervalSerializer(null)); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText.UnitTests/ExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Runtime.Serialization; 4 | using NodaTime.Testing; 5 | using ServiceStack.Text; 6 | using Xunit; 7 | 8 | namespace NodaTime.Serialization.ServiceStackText.UnitTests 9 | { 10 | public class ExtensionsTests 11 | { 12 | public ExtensionsTests() 13 | { 14 | //ServiceStack.Text Json serializer is static, need to setup the serializers for some of these tests. 15 | DateTimeZoneProviders.Tzdb.CreateDefaultSerializersForNodaTime().ConfigureSerializersForNodaTime(); 16 | } 17 | 18 | [Fact] 19 | public void CreateDefaultSerializersForNodaTime_NullDateTimeZoneProvider_Throws() 20 | { 21 | IDateTimeZoneProvider provider = null; 22 | Assert.Throws(() => provider.CreateDefaultSerializersForNodaTime()); 23 | } 24 | 25 | [Fact] 26 | public void CreateDefaultSerializersForNodaTime_ValidProvider_SettingsCreated() 27 | { 28 | Assert.NotNull(DateTimeZoneProviders.Tzdb.CreateDefaultSerializersForNodaTime()); 29 | } 30 | 31 | [Fact] 32 | public void SetSerializer_NonNullSettingsNullConfig_NoException() 33 | { 34 | INodaSerializerSettings settings = DateTimeZoneProviders.Tzdb.CreateDefaultSerializersForNodaTime(); 35 | Assert.NotNull(settings.SetSerializer(null)); 36 | } 37 | 38 | [Fact] 39 | public void SetSerializer_NullSettingsAndConfig_ReturnsNull() 40 | { 41 | INodaSerializerSettings settings = null; 42 | Assert.Null(settings.SetSerializer(null)); 43 | } 44 | 45 | [Fact] 46 | public void SetSerializer_NullSettingsNonNullConfig_ReturnNull() 47 | { 48 | INodaSerializerSettings settings = null; 49 | Assert.Null(settings.SetSerializer(s => { s.PeriodSerializer = null; })); 50 | } 51 | 52 | [Fact] 53 | public void WithIsoIntervalSerializer_ValidSettings_Verify() 54 | { 55 | INodaSerializerSettings settings = 56 | DateTimeZoneProviders.Tzdb.CreateDefaultSerializersForNodaTime().WithIsoIntervalSerializer(); 57 | Assert.Same(NodaSerializerDefinitions.ExtendedIsoIntervalSerializer, settings.IntervalSerializer); 58 | } 59 | 60 | [Fact] 61 | public void WithNormalizingIsoPeriodSerializer_ValidSettings_Verify() 62 | { 63 | INodaSerializerSettings settings = 64 | DateTimeZoneProviders.Tzdb.CreateDefaultSerializersForNodaTime().WithNormalizingIsoPeriodSerializer(); 65 | Assert.Same(NodaSerializerDefinitions.NormalizingIsoPeriodSerializer, settings.PeriodSerializer); 66 | } 67 | 68 | [Fact] 69 | public void ConfigureSerializersForNodaTime_NullSettings_NoException() 70 | { 71 | INodaSerializerSettings settings = null; 72 | settings.ConfigureSerializersForNodaTime(); 73 | } 74 | 75 | [Fact] 76 | public void ConfigureSerializer_Nullserializer_NoException() 77 | { 78 | IServiceStackSerializer serializer = null; 79 | serializer.ConfigureSerializer(); 80 | } 81 | 82 | [Fact] 83 | public void ConfigureSerializersForNodaTime_Default_VerifyConfiguration() 84 | { 85 | AssertSerializeAndDeserializeFunctions(NodaSerializerDefinitions.DurationSerializer); 86 | AssertSerializeAndDeserializeFunctions(NodaSerializerDefinitions.InstantSerializer); 87 | AssertSerializeAndDeserializeFunctions(NodaSerializerDefinitions.LocalDateSerializer); 88 | AssertSerializeAndDeserializeFunctions(NodaSerializerDefinitions.LocalDateTimeSerializer); 89 | AssertSerializeAndDeserializeFunctions(NodaSerializerDefinitions.LocalTimeSerializer); 90 | AssertSerializeAndDeserializeFunctions(NodaSerializerDefinitions.RoundtripPeriodSerializer); 91 | AssertSerializeAndDeserializeFunctions(NodaSerializerDefinitions.OffsetSerializer); 92 | AssertSerializeAndDeserializeFunctions(NodaSerializerDefinitions.OffsetDateTimeSerializer); 93 | AssertRawSerializeAndDeserializeFunctions(NodaSerializerDefinitions.ComplexIntervalSerializer); 94 | } 95 | 96 | [Fact] 97 | public void DeserializeFromString_ValidNullableLocalDateText_ReturnsValidNullableLocaldate() 98 | { 99 | var expectedLocalDate = new LocalDate(2014, 2, 21); 100 | var actualLocalDate = JsonSerializer.DeserializeFromString("2014-02-21"); 101 | Assert.Equal(expectedLocalDate, actualLocalDate); 102 | } 103 | 104 | [Fact] 105 | public void DeserializeFromString_BadNullableLocalDateText_Throws() 106 | { 107 | Assert.Throws(() => JsonSerializer.DeserializeFromString("Wrong")); 108 | } 109 | 110 | [Fact] 111 | public void DeserializeFromString_NullNullableLocalDateText_ReturnsNull() 112 | { 113 | Assert.Null(JsonSerializer.DeserializeFromString(null)); 114 | } 115 | 116 | [Fact] 117 | public void DeserializeFromString_EmptyNullableLocalDateText_ReturnsNull() 118 | { 119 | Assert.Null(JsonSerializer.DeserializeFromString(string.Empty)); 120 | } 121 | 122 | [Fact] 123 | public void DeserializeFromString_WhitespaceNullableLocalDateText_Throws() 124 | { 125 | Assert.Throws(() => JsonSerializer.DeserializeFromString(" \r \n ")); 126 | } 127 | 128 | [Fact] 129 | public void Serialize_NullNullableLocalDate_NullText() 130 | { 131 | Assert.Null(JsonSerializer.SerializeToString(null)); 132 | } 133 | 134 | [Fact] 135 | public void Serialize_ValidNullableLocalDate_ValidText() 136 | { 137 | var text = JsonSerializer.SerializeToString(new LocalDate(2014, 2, 21)); 138 | Assert.Equal("\"2014-02-21\"", text); 139 | } 140 | 141 | [Fact] 142 | public void DeserializeFromString_ValidNullableIntervalText_ReturnsValidNullableInterval() 143 | { 144 | var start = Instant.FromUtc(2014, 2, 21, 2, 21); 145 | var end = Instant.FromUtc(2014, 2, 21, 21, 2); 146 | var expectedInterval = new Interval(start, end); 147 | var actualInterval = 148 | JsonSerializer.DeserializeFromString( 149 | "{\"Start\":\"2014-02-21T02:21:00Z\",\"End\":\"2014-02-21T21:02:00Z\"}"); 150 | Assert.Equal(expectedInterval, actualInterval); 151 | } 152 | 153 | [Fact] 154 | public void DeserializeFromString_BadNullableIntervalText_Throws() 155 | { 156 | Assert.Throws(() => JsonSerializer.DeserializeFromString("Wrong")); 157 | } 158 | 159 | [Fact] 160 | public void DeserializeFromString_NullNullableIntervalText_ReturnsNull() 161 | { 162 | Assert.Null(JsonSerializer.DeserializeFromString(null)); 163 | } 164 | 165 | [Fact] 166 | public void DeserializeFromString_EmptyNullableIntervalText_ReturnsNull() 167 | { 168 | Assert.Null(JsonSerializer.DeserializeFromString(string.Empty)); 169 | } 170 | 171 | [Fact] 172 | public void DeserializeFromString_WhitespaceNullableIntervalText_Throws() 173 | { 174 | Assert.Throws(() => JsonSerializer.DeserializeFromString(" \n ")); 175 | } 176 | 177 | [Fact] 178 | public void Serialize_NullNullableInterval_NullText() 179 | { 180 | Assert.Null(JsonSerializer.SerializeToString(null)); 181 | } 182 | 183 | [Fact] 184 | public void Serialize_ValidNullableInterval_ValidText() 185 | { 186 | var start = Instant.FromUtc(2014, 2, 21, 2, 21); 187 | var end = Instant.FromUtc(2014, 2, 21, 21, 2); 188 | var text = JsonSerializer.SerializeToString(new Interval(start, end)); 189 | Assert.Equal("{\"Start\":\"2014-02-21T02:21:00Z\",\"End\":\"2014-02-21T21:02:00Z\"}", text); 190 | } 191 | 192 | [Fact] 193 | public void WithGeneralIsoZonedDateTimeSerializer_Serialize() 194 | { 195 | var clock = FakeClock.FromUtc(2014, 05, 02, 10, 30, 45); 196 | var now = clock.GetCurrentInstant().InZone(DateTimeZoneProviders.Tzdb.GetSystemDefault()); 197 | var serialisers = new DefaultNodaSerializerSettings(DateTimeZoneProviders.Tzdb) 198 | .WithGeneralIsoZonedDateTimeSerializer(); 199 | 200 | var expected = now.ToString(); 201 | var actual = serialisers.ZonedDateTimeSerializer.Serialize(now); 202 | 203 | Assert.Equal(expected, actual); 204 | } 205 | 206 | [Fact] 207 | public void WithGeneralIsoZonedDateTimeSerializer_Deserialize() 208 | { 209 | var clock = FakeClock.FromUtc(2014, 05, 02, 10, 30, 45); 210 | var now = clock.GetCurrentInstant().InZone(DateTimeZoneProviders.Tzdb.GetSystemDefault()); 211 | var serialisers = new DefaultNodaSerializerSettings(DateTimeZoneProviders.Tzdb) 212 | .WithGeneralIsoZonedDateTimeSerializer(); 213 | 214 | var expected = now; 215 | var actual = serialisers.ZonedDateTimeSerializer.Deserialize(now.ToString()); 216 | 217 | Assert.Equal(expected, actual); 218 | } 219 | 220 | private void AssertSerializeAndDeserializeFunctions(IServiceStackSerializer serializer) 221 | { 222 | Func serializationFunc = serializer.Serialize; 223 | Func deserializationFunc = serializer.Deserialize; 224 | Assert.Same(JsConfig.SerializeFn.Target, serializationFunc.Target); 225 | Assert.Same(GetDeserializerTarget(), deserializationFunc.Target); 226 | } 227 | 228 | private void AssertRawSerializeAndDeserializeFunctions(IServiceStackSerializer serializer) 229 | { 230 | Func serializationFunc = serializer.Serialize; 231 | Func deserializationFunc = serializer.Deserialize; 232 | Assert.Same(JsConfig.RawSerializeFn.Target, serializationFunc.Target); 233 | Assert.Same(GetRawDeserializerTarget(), deserializationFunc.Target); 234 | } 235 | 236 | private static object GetDeserializerTarget() 237 | { 238 | return GetDeserializerTarget(nameof(JsConfig.DeSerializeFn)); 239 | } 240 | 241 | private static object GetRawDeserializerTarget() 242 | { 243 | return GetDeserializerTarget(nameof(JsConfig.RawDeserializeFn)); 244 | } 245 | 246 | private static object GetDeserializerTarget(string name) 247 | { 248 | var field = typeof(JsConfig).GetRuntimeField(name); 249 | object value; 250 | if (field != null) 251 | { 252 | value = field.GetValue(null); 253 | } 254 | else 255 | { 256 | var property = typeof(JsConfig).GetRuntimeProperty(name); 257 | value = property.GetValue(null, null); 258 | } 259 | 260 | return ((Func)value).Target; 261 | } 262 | } 263 | } -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText.UnitTests/NodaSerializerDefinitionsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NodaTime.TimeZones; 3 | using Xunit; 4 | 5 | namespace NodaTime.Serialization.ServiceStackText.UnitTests 6 | { 7 | public class NodaSerializerDefinitionsTests 8 | { 9 | [Fact] 10 | public void CreateZonedDateTimeSerializer_NullPattern_Throws() 11 | { 12 | Assert.Throws( 13 | () => 14 | NodaSerializerDefinitions.CreateZonedDateTimeSerializer( 15 | new DateTimeZoneCache(TzdbDateTimeZoneSource.Default), 16 | null)); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText.UnitTests/NodaTime.Serialization.ServiceStackText.UnitTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.0;net452 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText.UnitTests/ServiceStackFallbackDeserializersTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | using Xunit; 4 | 5 | namespace NodaTime.Serialization.ServiceStackText.UnitTests 6 | { 7 | public class ServiceStackFallbackDeserializersTests 8 | { 9 | [Fact] 10 | public void ToAnnualDate_ValidText_Deserialize() 11 | { 12 | DeserializeAssert( 13 | ServiceStackFallbackDeserializers.ToAnnualDate, 14 | "2019-12-05 19:12:05Z", 15 | new AnnualDate(12,5)); 16 | } 17 | 18 | [Fact] 19 | public void ToAnnualDate_MalformedText_Throws() 20 | { 21 | MalformedTextAssert(ServiceStackFallbackDeserializers.ToAnnualDate); 22 | } 23 | 24 | [Fact] 25 | public void ToAnnualDate_NullText_Throws() 26 | { 27 | NullTextAssert(ServiceStackFallbackDeserializers.ToAnnualDate); 28 | } 29 | 30 | [Fact] 31 | public void ToInstant_ValidText_Deserialize() 32 | { 33 | DeserializeAssert( 34 | ServiceStackFallbackDeserializers.ToInstant, 35 | "2014-2-21 19:02:13Z", 36 | Instant.FromDateTimeOffset(new DateTimeOffset(2014, 2, 21, 19, 2, 13, TimeSpan.Zero))); 37 | } 38 | 39 | [Fact] 40 | public void ToInstant_MalformedText_Throws() 41 | { 42 | MalformedTextAssert(ServiceStackFallbackDeserializers.ToInstant); 43 | } 44 | 45 | [Fact] 46 | public void ToInstant_NullText_Throws() 47 | { 48 | NullTextAssert(ServiceStackFallbackDeserializers.ToInstant); 49 | } 50 | 51 | [Fact] 52 | public void ToLocalTime_ValidText_Deserialize() 53 | { 54 | DeserializeAssert( 55 | ServiceStackFallbackDeserializers.ToLocalTime, 56 | "7:02:13", 57 | new LocalTime(7,2,13)); 58 | } 59 | 60 | [Fact] 61 | public void ToLocalTime_MalformedText_Throws() 62 | { 63 | MalformedTextAssert(ServiceStackFallbackDeserializers.ToLocalTime); 64 | } 65 | 66 | [Fact] 67 | public void ToLocalTime_NullText_Throws() 68 | { 69 | NullTextAssert(ServiceStackFallbackDeserializers.ToLocalTime); 70 | } 71 | 72 | [Fact] 73 | public void ToLocalDate_ValidText_Deserialize() 74 | { 75 | DeserializeAssert( 76 | ServiceStackFallbackDeserializers.ToLocalDate, 77 | "2014-2-21", 78 | new LocalDate(2014,2,21)); 79 | } 80 | 81 | [Fact] 82 | public void ToLocalDate_MalformedText_Throws() 83 | { 84 | MalformedTextAssert(ServiceStackFallbackDeserializers.ToLocalDate); 85 | } 86 | 87 | [Fact] 88 | public void ToLocalDate_NullText_Throws() 89 | { 90 | NullTextAssert(ServiceStackFallbackDeserializers.ToLocalDate); 91 | } 92 | 93 | [Fact] 94 | public void ToLocalDateTime_ValidText_Deserialize() 95 | { 96 | DeserializeAssert( 97 | ServiceStackFallbackDeserializers.ToLocalDateTime, 98 | "2014/2/21 19:02:13", 99 | new LocalDateTime(2014, 2, 21, 19, 2, 13)); 100 | } 101 | 102 | [Fact] 103 | public void ToLocalDateTime_MalformedText_Throws() 104 | { 105 | MalformedTextAssert(ServiceStackFallbackDeserializers.ToLocalDateTime); 106 | } 107 | 108 | [Fact] 109 | public void ToLocalDateTime_NullText_Throws() 110 | { 111 | NullTextAssert(ServiceStackFallbackDeserializers.ToLocalDateTime); 112 | } 113 | 114 | [Fact] 115 | public void ToOffsetDateTime_ValidText_Deserialize() 116 | { 117 | DeserializeAssert( 118 | ServiceStackFallbackDeserializers.ToOffsetDateTime, 119 | "2014/2/21 19:02:13 +05:00", 120 | new OffsetDateTime(new LocalDateTime(2014, 2, 21, 19, 2, 13), Offset.FromHours(5))); 121 | } 122 | 123 | [Fact] 124 | public void ToOffsetDateTime_MalformedText_Throws() 125 | { 126 | MalformedTextAssert(ServiceStackFallbackDeserializers.ToOffsetDateTime); 127 | } 128 | 129 | [Fact] 130 | public void ToOffsetDateTime_NullText_Throws() 131 | { 132 | NullTextAssert(ServiceStackFallbackDeserializers.ToOffsetDateTime); 133 | } 134 | 135 | [Fact] 136 | public void ToZonedDateTime_ValidText_Deserialize() 137 | { 138 | DeserializeAssert( 139 | ServiceStackFallbackDeserializers.ToZonedDateTime, 140 | "2014/2/21 19:02:13 +05:00", 141 | ZonedDateTime.FromDateTimeOffset(new DateTimeOffset(2014, 2, 21, 19, 2, 13, TimeSpan.FromHours(5)))); 142 | } 143 | 144 | [Fact] 145 | public void ToZonedDateTime_MalformedText_Throws() 146 | { 147 | MalformedTextAssert(ServiceStackFallbackDeserializers.ToZonedDateTime); 148 | } 149 | 150 | [Fact] 151 | public void ToZonedDateTime_NullText_Throws() 152 | { 153 | NullTextAssert(ServiceStackFallbackDeserializers.ToZonedDateTime); 154 | } 155 | 156 | private static void DeserializeAssert(Func fallbackSerializer, string text, T expected) 157 | { 158 | var actual = fallbackSerializer(text); 159 | Assert.Equal(expected, actual); 160 | } 161 | private static void MalformedTextAssert(Func fallbackSerializer) 162 | { 163 | Assert.Throws(() => fallbackSerializer("JustPlainWrong")); 164 | } 165 | 166 | private static void NullTextAssert(Func fallbackSerializer) 167 | { 168 | Assert.Throws(() => fallbackSerializer(null)); 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText.UnitTests/StandardServiceStackSerializerTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NodaTime.Text; 3 | using Xunit; 4 | 5 | namespace NodaTime.Serialization.ServiceStackText.UnitTests 6 | { 7 | public class StandardServiceStackSerializerTests 8 | { 9 | [Fact] 10 | public void AnnualDateConverter() 11 | { 12 | var value = new AnnualDate(12, 5); 13 | string json = "12-05"; 14 | AssertConversions(value, json, NodaSerializerDefinitions.AnnualDateSerializer); 15 | } 16 | 17 | [Fact] 18 | public void Constructor_NullPattern_ArgumentNullException() 19 | { 20 | Assert.Throws(() => new StandardServiceStackSerializer(null)); 21 | } 22 | 23 | [Fact] 24 | public void OffsetConverter() 25 | { 26 | var value = Offset.FromHoursAndMinutes(5, 30); 27 | string json = "+05:30"; 28 | AssertConversions(value, json, NodaSerializerDefinitions.OffsetSerializer); 29 | } 30 | 31 | [Fact] 32 | public void InstantConverter() 33 | { 34 | var value = Instant.FromUtc(2012, 1, 2, 3, 4, 5); 35 | string json = "2012-01-02T03:04:05Z"; 36 | AssertConversions(value, json,NodaSerializerDefinitions.InstantSerializer); 37 | } 38 | 39 | [Fact] 40 | public void LocalDateConverter() 41 | { 42 | var value = new LocalDate(2012, 1, 2, CalendarSystem.Iso); 43 | string json = "2012-01-02"; 44 | AssertConversions(value, json, NodaSerializerDefinitions.LocalDateSerializer); 45 | } 46 | 47 | [Fact] 48 | public void LocalDateConverter_SerializeNonIso_Throws() 49 | { 50 | var localDate = new LocalDate(2012, 1, 2, CalendarSystem.Coptic);//.GetCopticCalendar(4)); 51 | 52 | Assert.Throws(() => NodaSerializerDefinitions.LocalDateSerializer.Serialize(localDate)); 53 | } 54 | 55 | [Fact] 56 | public void LocalDateTimeConverter() 57 | { 58 | var value = new LocalDateTime(2012, 1, 2, 3, 4, 5, 6, CalendarSystem.Iso); 59 | var json = "2012-01-02T03:04:05.006"; 60 | AssertConversions(value, json,NodaSerializerDefinitions.LocalDateTimeSerializer); 61 | } 62 | 63 | [Fact] 64 | public void Deserialize_NoFallback_Throws() 65 | { 66 | var serviceStackSerializer = new StandardServiceStackSerializer(LocalDatePattern.Iso); 67 | Assert.Throws(() => serviceStackSerializer.Deserialize("Invalid")); 68 | } 69 | 70 | [Fact] 71 | public void Deserialize_PoorlyFormedText_Deserialized() 72 | { 73 | var localDate = NodaSerializerDefinitions.LocalDateSerializer.Deserialize("2014/1/2"); 74 | var expectedLocalDate = new LocalDate(2014, 1, 2); 75 | Assert.Equal(expectedLocalDate, localDate); 76 | } 77 | 78 | [Fact] 79 | public void Deserialize_NoFallbackPoorlyFormedText_Throws() 80 | { 81 | var serializer = new StandardServiceStackSerializer(LocalDatePattern.Iso); 82 | Assert.Throws(() => serializer.Deserialize("2014/1/2")); 83 | } 84 | 85 | [Fact] 86 | public void UseRawSerializer_StandardSerializer_False() 87 | { 88 | var serializer = new StandardServiceStackSerializer(LocalDatePattern.Iso); 89 | Assert.False(serializer.UseRawSerializer); 90 | } 91 | 92 | [Fact] 93 | public void LocalDateTimeConverter_SerializeNonIso_Throws() 94 | { 95 | var localDateTime = new LocalDateTime(2012, 1, 2, 3, 4, 5, CalendarSystem.Coptic);//.GetCopticCalendar(4)); 96 | 97 | Assert.Throws( 98 | () => NodaSerializerDefinitions.LocalDateTimeSerializer.Serialize(localDateTime)); 99 | } 100 | 101 | [Fact] 102 | public void LocalTimeConverter() 103 | { 104 | var value = new LocalTime(1, 2, 3, 4); 105 | var json = "01:02:03.004"; 106 | AssertConversions(value, json,NodaSerializerDefinitions.LocalTimeSerializer); 107 | } 108 | 109 | [Fact] 110 | public void RoundtripPeriodConverter() 111 | { 112 | var value = Period.FromDays(2) + Period.FromHours(3) + Period.FromMinutes(90); 113 | string json = "P2DT3H90M"; 114 | AssertConversions(value, json,NodaSerializerDefinitions.RoundtripPeriodSerializer); 115 | } 116 | 117 | [Fact] 118 | public void NormalizingIsoPeriodConverter_RequiresNormalization() 119 | { 120 | // Can't use AssertConversions here, as it doesn't round-trip 121 | var period = Period.FromDays(2) + Period.FromHours(3) + Period.FromMinutes(90); 122 | 123 | var json = NodaSerializerDefinitions.NormalizingIsoPeriodSerializer.Serialize(period); 124 | string expectedJson = "P2DT4H30M"; 125 | Assert.Equal(expectedJson, json); 126 | } 127 | 128 | [Fact] 129 | public void NormalizingIsoPeriodConverter_AlreadyNormalized() 130 | { 131 | // This time we're okay as it's already a normalized value. 132 | var value = Period.FromDays(2) + Period.FromHours(4) + Period.FromMinutes(30); 133 | string json = "P2DT4H30M"; 134 | AssertConversions(value, json, NodaSerializerDefinitions.NormalizingIsoPeriodSerializer); 135 | } 136 | 137 | [Fact] 138 | public void ZonedDateTimeConverter() 139 | { 140 | // Deliberately give it an ambiguous local time, in both ways. 141 | var zone = DateTimeZoneProviders.Tzdb["Europe/London"]; 142 | var earlierValue = new ZonedDateTime(new LocalDateTime(2012, 10, 28, 1, 30), zone, Offset.FromHours(1)); 143 | var laterValue = new ZonedDateTime(new LocalDateTime(2012, 10, 28, 1, 30), zone, Offset.FromHours(0)); 144 | string earlierJson = "2012-10-28T01:30:00+01 Europe/London"; 145 | string laterJson = "2012-10-28T01:30:00Z Europe/London"; 146 | var converter = NodaSerializerDefinitions.CreateZonedDateTimeSerializer(DateTimeZoneProviders.Tzdb); 147 | 148 | AssertConversions(earlierValue, earlierJson, converter); 149 | AssertConversions(laterValue, laterJson, converter); 150 | } 151 | 152 | [Fact] 153 | public void OffsetDateTimeConverter() 154 | { 155 | var value = new LocalDateTime(2012, 1, 2, 3, 4, 5, 6).WithOffset(Offset.FromHoursAndMinutes(-1, -30) + Offset.FromMilliseconds(-1234)); 156 | string json = "2012-01-02T03:04:05.006-01:30:01"; 157 | AssertConversions(value, json, NodaSerializerDefinitions.OffsetDateTimeSerializer); 158 | } 159 | 160 | [Fact] 161 | public void Duration_WholeSeconds() 162 | { 163 | AssertConversions(Duration.FromHours(48), "48:00:00", NodaSerializerDefinitions.DurationSerializer); 164 | } 165 | 166 | [Fact] 167 | public void Duration_FractionalSeconds() 168 | { 169 | AssertConversions(Duration.FromHours(48) + Duration.FromSeconds(3) + Duration.FromTicks(1234567), "48:00:03.1234567", NodaSerializerDefinitions.DurationSerializer); 170 | AssertConversions(Duration.FromHours(48) + Duration.FromSeconds(3) + Duration.FromTicks(1230000), "48:00:03.123", NodaSerializerDefinitions.DurationSerializer); 171 | AssertConversions(Duration.FromHours(48) + Duration.FromSeconds(3) + Duration.FromTicks(1234000), "48:00:03.1234", NodaSerializerDefinitions.DurationSerializer); 172 | AssertConversions(Duration.FromHours(48) + Duration.FromSeconds(3) + Duration.FromTicks(12345), "48:00:03.0012345", NodaSerializerDefinitions.DurationSerializer); 173 | } 174 | 175 | [Fact] 176 | public void Duration_MinAndMaxValues() 177 | { 178 | AssertConversions(Duration.FromTicks(long.MaxValue), "256204778:48:05.4775807", NodaSerializerDefinitions.DurationSerializer); 179 | AssertConversions(Duration.FromTicks(long.MinValue), "-256204778:48:05.4775808", NodaSerializerDefinitions.DurationSerializer); 180 | } 181 | 182 | [Fact] 183 | public void Duration_ParsePartialFractionalSecondsWithTrailingZeroes() 184 | { 185 | var parsed = NodaSerializerDefinitions.DurationSerializer.Deserialize("25:10:00.1234000"); 186 | Assert.Equal(Duration.FromHours(25) + Duration.FromMinutes(10) + Duration.FromTicks(1234000), parsed); 187 | } 188 | 189 | private static void AssertConversions(T value, string expectedJson, IServiceStackSerializer serializer) 190 | { 191 | var actualJson = serializer.Serialize(value); 192 | Assert.Equal(expectedJson, actualJson); 193 | 194 | var deserializedValue = serializer.Deserialize(expectedJson); 195 | Assert.Equal(value, deserializedValue); 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29519.181 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NodaTime.Serialization.ServiceStackText", "NodaTime.Serialization.ServiceStackText\NodaTime.Serialization.ServiceStackText.csproj", "{85C67FEF-0293-49C6-A34A-AB492C750226}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NodaTime.Serialization.ServiceStackText.UnitTests", "NodaTime.Serialization.ServiceStackText.UnitTests\NodaTime.Serialization.ServiceStackText.UnitTests.csproj", "{05DECB1D-FDCB-4835-B6D5-F4CE51145263}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BC4AAF45-1EB6-4844-8481-7544B04DE170}" 11 | ProjectSection(SolutionItems) = preProject 12 | ..\build.bat = ..\build.bat 13 | ..\build.cake = ..\build.cake 14 | ..\cake.config = ..\cake.config 15 | Directory.Build.NugetPackage.props = Directory.Build.NugetPackage.props 16 | Directory.Build.props = Directory.Build.props 17 | Directory.Build.SourceLink.props = Directory.Build.SourceLink.props 18 | Directory.Build.Version.props = Directory.Build.Version.props 19 | ..\LICENSE = ..\LICENSE 20 | ..\LICENSE_NodaTime = ..\LICENSE_NodaTime 21 | ..\LICENSE_ServiceStack = ..\LICENSE_ServiceStack 22 | ..\README.md = ..\README.md 23 | ..\version.json = ..\version.json 24 | EndProjectSection 25 | EndProject 26 | Global 27 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 28 | Debug|Any CPU = Debug|Any CPU 29 | Release|Any CPU = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 32 | {85C67FEF-0293-49C6-A34A-AB492C750226}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {85C67FEF-0293-49C6-A34A-AB492C750226}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {85C67FEF-0293-49C6-A34A-AB492C750226}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {85C67FEF-0293-49C6-A34A-AB492C750226}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {05DECB1D-FDCB-4835-B6D5-F4CE51145263}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {05DECB1D-FDCB-4835-B6D5-F4CE51145263}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {05DECB1D-FDCB-4835-B6D5-F4CE51145263}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {05DECB1D-FDCB-4835-B6D5-F4CE51145263}.Release|Any CPU.Build.0 = Release|Any CPU 40 | EndGlobalSection 41 | GlobalSection(SolutionProperties) = preSolution 42 | HideSolutionNode = FALSE 43 | EndGlobalSection 44 | GlobalSection(ExtensibilityGlobals) = postSolution 45 | SolutionGuid = {A9DBFD07-2EF2-46C3-ADCD-D72FABF2E10B} 46 | EndGlobalSection 47 | EndGlobal 48 | -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText/ComplexJsonIntervalSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NodaTime.Utility; 3 | using ServiceStack.Text; 4 | 5 | namespace NodaTime.Serialization.ServiceStackText 6 | { 7 | /// 8 | /// ServiceStack.Text JSON serializer for using a compound representation. The start and 9 | /// end aspects of the interval are represented with separate properties, each parsed and formatted 10 | /// by the serializer provided. 11 | /// 12 | public class ComplexJsonIntervalSerializer : IServiceStackSerializer 13 | { 14 | private readonly IServiceStackSerializer _instantSerializer; 15 | 16 | /// 17 | /// uses the ServiceStack.Text raw serializer. 18 | /// 19 | public bool UseRawSerializer 20 | { 21 | get { return true; } 22 | } 23 | 24 | /// 25 | /// Creates a new instance of an serializer that uses a complex JSON representation. 26 | /// 27 | /// The serializer to use to parse and format the start and 28 | /// end . 29 | public ComplexJsonIntervalSerializer(IServiceStackSerializer instantSerializer) 30 | { 31 | if (instantSerializer == null) 32 | { 33 | throw new ArgumentNullException(nameof(instantSerializer)); 34 | } 35 | this._instantSerializer = instantSerializer; 36 | } 37 | 38 | /// 39 | /// Serializes the provided . 40 | /// 41 | /// The to to serialize. 42 | /// The serialized representation. 43 | public string Serialize(Interval value) 44 | { 45 | var complexIntervalDto = new ComplexRawIntervalDto 46 | { 47 | Start = _instantSerializer.Serialize(value.Start), 48 | End = _instantSerializer.Serialize(value.End) 49 | }; 50 | 51 | return JsonSerializer.SerializeToString(complexIntervalDto); 52 | } 53 | 54 | /// 55 | /// Deserializes the given JSON. 56 | /// 57 | /// The JSON to parse. 58 | /// The deserialized . 59 | public Interval Deserialize(string text) 60 | { 61 | var complexIntervalDto = JsonSerializer.DeserializeFromString(text); 62 | 63 | if (!IsValid(complexIntervalDto)) 64 | { 65 | throw new InvalidNodaDataException("An Interval must contain Start and End properties."); 66 | } 67 | 68 | var start = _instantSerializer.Deserialize(complexIntervalDto.Start); 69 | var end = _instantSerializer.Deserialize(complexIntervalDto.End); 70 | 71 | var interval = new Interval(start, end); 72 | 73 | return interval; 74 | } 75 | 76 | private static bool IsValid(ComplexRawIntervalDto complexIntervalDto) 77 | { 78 | if (complexIntervalDto == null) 79 | { 80 | return false; 81 | } 82 | if (string.IsNullOrEmpty(complexIntervalDto.Start)) 83 | { 84 | return false; 85 | } 86 | return !string.IsNullOrEmpty(complexIntervalDto.End); 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText/ComplexRawIntervalDto.cs: -------------------------------------------------------------------------------- 1 | namespace NodaTime.Serialization.ServiceStackText 2 | { 3 | internal class ComplexRawIntervalDto 4 | { 5 | public string Start { get; set; } 6 | public string End { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText/DateTimeZoneSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using NodaTime.TimeZones; 4 | 5 | namespace NodaTime.Serialization.ServiceStackText 6 | { 7 | /// 8 | /// ServiceStack.Text JSON serializer for for the given . 9 | /// Deserialization is case insensitive. 10 | /// 11 | public class DateTimeZoneSerializer : IServiceStackSerializer 12 | { 13 | private readonly IDateTimeZoneProvider _provider; 14 | 15 | /// 16 | /// The does not use the raw serializer. 17 | /// 18 | public bool UseRawSerializer { get { return false; } } 19 | 20 | /// 21 | /// Creates an instance of the serializer for the given . 22 | /// 23 | /// The to use. 24 | public DateTimeZoneSerializer(IDateTimeZoneProvider provider) 25 | { 26 | if (provider == null) 27 | { 28 | throw new ArgumentNullException("provider"); 29 | } 30 | 31 | this._provider = provider; 32 | } 33 | 34 | /// 35 | /// Serializes the provided . 36 | /// 37 | /// The to serialize. 38 | /// The serialized representation. 39 | public string Serialize(DateTimeZone value) 40 | { 41 | return value == null ? null : value.Id; 42 | } 43 | 44 | /// 45 | /// Deserializes the given JSON. 46 | /// 47 | /// The JSON to parse. 48 | /// The deserialized . 49 | public DateTimeZone Deserialize(string text) 50 | { 51 | var id = _provider.Ids.FirstOrDefault(s => String.Equals(text, s, StringComparison.OrdinalIgnoreCase)); 52 | if (string.IsNullOrEmpty(id)) 53 | { 54 | throw new DateTimeZoneNotFoundException("Time zone " + text + " is unknown."); 55 | } 56 | return _provider[id]; 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText/DefaultNodaSerializerSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace NodaTime.Serialization.ServiceStackText 4 | { 5 | /// 6 | /// A collection of typical ServiceStack.Text JSON serializers for use with NodaTime. 7 | /// 8 | public class DefaultNodaSerializerSettings : INodaSerializerSettings 9 | { 10 | /// 11 | /// The serializer to use. 12 | /// 13 | public IServiceStackSerializer AnnualDateSerializer { get; set; } 14 | 15 | /// 16 | /// The serializer to use. 17 | /// 18 | public IServiceStackSerializer DateTimeZoneSerializer { get; set; } 19 | 20 | /// 21 | /// The serializer to use. 22 | /// 23 | public IServiceStackSerializer DurationSerializer { get; set; } 24 | 25 | /// 26 | /// The serializer to use. 27 | /// 28 | public IServiceStackSerializer InstantSerializer { get; set; } 29 | 30 | /// 31 | /// The serializer to use. 32 | /// 33 | public IServiceStackSerializer IntervalSerializer { get;set; } 34 | 35 | /// 36 | /// The serializer to use. 37 | /// 38 | public IServiceStackSerializer LocalDateSerializer { get; set; } 39 | 40 | /// 41 | /// The serializer to use. 42 | /// 43 | public IServiceStackSerializer LocalDateTimeSerializer { get; set; } 44 | 45 | /// 46 | /// The serializer to use. 47 | /// 48 | public IServiceStackSerializer LocalTimeSerializer { get; set; } 49 | 50 | /// 51 | /// The serializer to use. 52 | /// 53 | public IServiceStackSerializer OffsetSerializer { get; set; } 54 | 55 | /// 56 | /// The serializer to use. 57 | /// 58 | public IServiceStackSerializer OffsetDateTimeSerializer { get; set; } 59 | 60 | /// 61 | /// The serializer to use. 62 | /// 63 | public IServiceStackSerializer PeriodSerializer { get; set; } 64 | 65 | /// 66 | /// The serializer to use. 67 | /// 68 | public IServiceStackSerializer ZonedDateTimeSerializer { get; set; } 69 | 70 | /// 71 | /// The in use. 72 | /// 73 | public IDateTimeZoneProvider Provider { get; set; } 74 | 75 | /// 76 | /// Creates an instance of default serializers using the given . 77 | /// 78 | /// 79 | public DefaultNodaSerializerSettings(IDateTimeZoneProvider provider) 80 | { 81 | if (provider == null) 82 | { 83 | throw new ArgumentNullException("provider"); 84 | } 85 | 86 | AnnualDateSerializer = NodaSerializerDefinitions.AnnualDateSerializer; 87 | DurationSerializer = NodaSerializerDefinitions.DurationSerializer; 88 | DateTimeZoneSerializer = NodaSerializerDefinitions.CreateDateTimeZoneSerializer(provider); 89 | InstantSerializer = NodaSerializerDefinitions.InstantSerializer; 90 | IntervalSerializer = NodaSerializerDefinitions.ComplexIntervalSerializer; 91 | LocalDateSerializer = NodaSerializerDefinitions.LocalDateSerializer; 92 | LocalDateTimeSerializer = NodaSerializerDefinitions.LocalDateTimeSerializer; 93 | LocalTimeSerializer = NodaSerializerDefinitions.LocalTimeSerializer; 94 | OffsetSerializer = NodaSerializerDefinitions.OffsetSerializer; 95 | OffsetDateTimeSerializer = NodaSerializerDefinitions.OffsetDateTimeSerializer; 96 | PeriodSerializer = NodaSerializerDefinitions.RoundtripPeriodSerializer; 97 | ZonedDateTimeSerializer = NodaSerializerDefinitions.CreateZonedDateTimeSerializer(provider); 98 | Provider = provider; 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText/ExtendedIsoIntervalSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NodaTime.Utility; 3 | 4 | namespace NodaTime.Serialization.ServiceStackText 5 | { 6 | /// 7 | /// ServiceStack.Text JSON serializer for using a ISO8601 Interval representation. The 8 | /// start and end aspects of the interval are each parsed and formatted by the serializer 9 | /// provided. 10 | /// 11 | public class ExtendedIsoIntervalSerializer : IServiceStackSerializer 12 | { 13 | internal const char Iso8601TimeIntervalSeparator = '/'; 14 | private readonly IServiceStackSerializer _instantSerializer; 15 | 16 | /// 17 | /// does not use the ServiceStack.Text raw serializer. 18 | /// 19 | public bool UseRawSerializer 20 | { 21 | get { return false; } 22 | } 23 | 24 | /// 25 | /// Creates an instance of the 26 | /// 27 | /// The serializer to use. 28 | public ExtendedIsoIntervalSerializer(IServiceStackSerializer instantSerializer) 29 | { 30 | if (instantSerializer == null) 31 | { 32 | throw new ArgumentNullException("instantSerializer"); 33 | } 34 | this._instantSerializer = instantSerializer; 35 | } 36 | 37 | /// 38 | /// Serializes the provided . 39 | /// 40 | /// The to to serialize. 41 | /// The serialized representation. 42 | public string Serialize(Interval value) 43 | { 44 | return string.Format( 45 | "{0}{1}{2}", 46 | _instantSerializer.Serialize(value.Start), 47 | Iso8601TimeIntervalSeparator, 48 | _instantSerializer.Serialize(value.End) 49 | ); 50 | } 51 | 52 | /// 53 | /// Deserializes the given JSON. 54 | /// 55 | /// The JSON to parse. 56 | /// The deserialized . 57 | public Interval Deserialize(string text) 58 | { 59 | if (string.IsNullOrEmpty(text)) 60 | { 61 | throw new InvalidNodaDataException("No text to parse."); 62 | } 63 | 64 | var slash = text.IndexOf(Iso8601TimeIntervalSeparator); 65 | if (slash == -1) 66 | { 67 | throw new InvalidNodaDataException("Expected ISO-8601-formatted interval; slash was missing."); 68 | } 69 | var startText = text.Substring(0, slash); 70 | var endText = text.Substring(slash + 1); 71 | 72 | var start = _instantSerializer.Deserialize(startText); 73 | var end = _instantSerializer.Deserialize(endText); 74 | return new Interval(start, end); 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using NodaTime.Text; 4 | using ServiceStack.Text; 5 | 6 | namespace NodaTime.Serialization.ServiceStackText 7 | { 8 | /// 9 | /// Useful extensions used to configure the ServiceStack.Text JSON serializer. 10 | /// 11 | public static class Extensions 12 | { 13 | private static readonly object Mutex = new object(); 14 | private static MethodInfo _nullableSerializerMethodInfo; 15 | 16 | private static MethodInfo NullableSerializerMethodInfo 17 | { 18 | get 19 | { 20 | return _nullableSerializerMethodInfo 21 | ?? (_nullableSerializerMethodInfo = 22 | typeof(Extensions).GetTypeInfo().GetDeclaredMethod(nameof(ConfigureNullableSerializer))); 23 | } 24 | } 25 | 26 | /// 27 | /// Used for fluent setting of serializerSettings. Nothing is done if the serializer or config action is null. 28 | /// 29 | /// The serializer to perform the action on. 30 | /// The configuration action to perform on the serializerSettings. 31 | /// The original value, for further chaining. 32 | public static INodaSerializerSettings SetSerializer( 33 | this INodaSerializerSettings serializerSettings, 34 | Action configAction) 35 | { 36 | if (serializerSettings == null || configAction == null) 37 | { 38 | //nothing to do 39 | return serializerSettings; 40 | } 41 | configAction(serializerSettings); 42 | return serializerSettings; 43 | } 44 | 45 | /// 46 | /// Configures the given serializer settings to use 47 | /// . 48 | /// Any other converters which can convert are removed from the serializer. 49 | /// 50 | /// The existing serializer settings to add Noda Time serializerSettings to. 51 | /// The original value, for further chaining. 52 | public static INodaSerializerSettings WithIsoIntervalSerializer(this INodaSerializerSettings serializerSettings) 53 | { 54 | serializerSettings.SetSerializer( 55 | x => x.IntervalSerializer = NodaSerializerDefinitions.ExtendedIsoIntervalSerializer); 56 | 57 | return serializerSettings; 58 | } 59 | 60 | /// 61 | /// Configures the given serializer settings to use 62 | /// . 63 | /// Any other converters which can convert are removed from the serializer. 64 | /// 65 | /// The existing serializer settings to add Noda Time serializerSettings to. 66 | /// The original value, for further chaining. 67 | public static INodaSerializerSettings WithNormalizingIsoPeriodSerializer( 68 | this INodaSerializerSettings serializerSettings) 69 | { 70 | serializerSettings.SetSerializer( 71 | x => x.PeriodSerializer = NodaSerializerDefinitions.NormalizingIsoPeriodSerializer); 72 | 73 | return serializerSettings; 74 | } 75 | 76 | /// 77 | /// Configures the serializer to use a format compatible 78 | /// with the ToString method. 79 | /// 80 | /// The existing serializer settings to add Noda Time serializerSettings to. 81 | /// The original value, for further chaining. 82 | public static INodaSerializerSettings WithGeneralIsoZonedDateTimeSerializer( 83 | this INodaSerializerSettings serializerSettings) 84 | { 85 | serializerSettings.SetSerializer( 86 | x => x.ZonedDateTimeSerializer = 87 | NodaSerializerDefinitions.CreateZonedDateTimeSerializer(x.Provider, 88 | ZonedDateTimePattern.GeneralFormatOnlyIso.PatternText)); 89 | 90 | return serializerSettings; 91 | } 92 | 93 | /// 94 | /// Configuration for ServiceStack.Text with everything required to properly serialize and deserialize NodaTime data 95 | /// types to and from json. 96 | /// 97 | /// The time zone provider to use when parsing time zones and zoned date/times. 98 | /// A new Noda serializer settings. 99 | public static INodaSerializerSettings CreateDefaultSerializersForNodaTime(this IDateTimeZoneProvider provider) 100 | { 101 | if (provider == null) 102 | { 103 | throw new ArgumentNullException(nameof(provider)); 104 | } 105 | return new DefaultNodaSerializerSettings(provider); 106 | } 107 | 108 | /// 109 | /// Configures the ServiceStack.Text json serializer. 110 | /// 111 | /// The serializer settings to use. 112 | /// 113 | /// Unable to find the deserializer property or field member on the ServiceStack.Text serializer. 114 | /// 115 | public static void ConfigureSerializersForNodaTime(this INodaSerializerSettings nodaSerializerSettings) 116 | { 117 | if (nodaSerializerSettings == null) 118 | { 119 | //nothing to do 120 | return; 121 | } 122 | nodaSerializerSettings.AnnualDateSerializer.ConfigureSerializer(); 123 | nodaSerializerSettings.DateTimeZoneSerializer.ConfigureSerializer(); 124 | nodaSerializerSettings.DurationSerializer.ConfigureSerializer(); 125 | nodaSerializerSettings.InstantSerializer.ConfigureSerializer(); 126 | nodaSerializerSettings.IntervalSerializer.ConfigureSerializer(); 127 | nodaSerializerSettings.LocalDateSerializer.ConfigureSerializer(); 128 | nodaSerializerSettings.LocalDateTimeSerializer.ConfigureSerializer(); 129 | nodaSerializerSettings.LocalTimeSerializer.ConfigureSerializer(); 130 | nodaSerializerSettings.OffsetDateTimeSerializer.ConfigureSerializer(); 131 | nodaSerializerSettings.OffsetSerializer.ConfigureSerializer(); 132 | nodaSerializerSettings.PeriodSerializer.ConfigureSerializer(); 133 | nodaSerializerSettings.ZonedDateTimeSerializer.ConfigureSerializer(); 134 | } 135 | 136 | /// 137 | /// Configures the ServiceStack.Text json serializer. 138 | /// 139 | /// The individual serializer to configure. 140 | /// 141 | /// Unable to find the deserializer property or field member on the ServiceStack.Text serializer. 142 | /// 143 | public static void ConfigureSerializer(this IServiceStackSerializer serializer) 144 | { 145 | if (serializer == null) 146 | { 147 | //nothing to do 148 | return; 149 | } 150 | 151 | //JsConfig is not thread safe. 152 | lock (Mutex) 153 | { 154 | if (serializer.UseRawSerializer) 155 | { 156 | JsConfig.RawSerializeFn = serializer.Serialize; 157 | JsConfigWrapper.SetRawDeserializerMember(serializer.Deserialize); 158 | } 159 | else 160 | { 161 | JsConfig.SerializeFn = serializer.Serialize; 162 | JsConfigWrapper.SetDeserializerMember(serializer.Deserialize); 163 | } 164 | 165 | var type = typeof (T); 166 | 167 | if (type.GetTypeInfo().IsValueType) 168 | { 169 | //register nullable 170 | var genericMethod = NullableSerializerMethodInfo.MakeGenericMethod(type); 171 | genericMethod.Invoke(serializer, new object[] {serializer}); 172 | } 173 | } 174 | } 175 | 176 | private static void ConfigureNullableSerializer(this IServiceStackSerializer serializer) where T : struct 177 | { 178 | //ServiceStack.Text will never use the serialize / deserialize fn if the value is null 179 | //or the text is null or empty. 180 | if (serializer.UseRawSerializer) 181 | { 182 | JsConfig.RawSerializeFn = arg => serializer.Serialize(arg.Value); 183 | JsConfigWrapper.SetRawDeserializerMember(s => serializer.Deserialize(s)); 184 | } 185 | else 186 | { 187 | JsConfig.SerializeFn = arg => serializer.Serialize(arg.Value); 188 | JsConfigWrapper.SetDeserializerMember(s => serializer.Deserialize(s)); 189 | } 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText/INodaSerializerSettings.cs: -------------------------------------------------------------------------------- 1 | namespace NodaTime.Serialization.ServiceStackText 2 | { 3 | /// 4 | /// Provides the ServiceStack.Text JSON serializers to use for NodaTime. 5 | /// 6 | public interface INodaSerializerSettings 7 | { 8 | /// 9 | /// The ServiceStack.Text JSON serializer to use. 10 | /// 11 | IServiceStackSerializer AnnualDateSerializer { get; set; } 12 | /// 13 | /// The ServiceStack.Text JSON serializer to use. 14 | /// 15 | IServiceStackSerializer DateTimeZoneSerializer { get; set; } 16 | /// 17 | /// The ServiceStack.Text JSON serializer to use. 18 | /// 19 | IServiceStackSerializer DurationSerializer { get; set; } 20 | /// 21 | /// The ServiceStack.Text JSON serializer to use. 22 | /// 23 | IServiceStackSerializer InstantSerializer { get; set; } 24 | /// 25 | /// The ServiceStack.Text JSON serializer to use. 26 | /// 27 | IServiceStackSerializer IntervalSerializer { get; set; } 28 | /// 29 | /// The ServiceStack.Text JSON serializer to use. 30 | /// 31 | IServiceStackSerializer LocalDateSerializer { get; set; } 32 | /// 33 | /// The ServiceStack.Text JSON serializer to use. 34 | /// 35 | IServiceStackSerializer LocalDateTimeSerializer { get; set; } 36 | /// 37 | /// The ServiceStack.Text JSON serializer to use. 38 | /// 39 | IServiceStackSerializer LocalTimeSerializer { get; set; } 40 | /// 41 | /// The ServiceStack.Text JSON serializer to use. 42 | /// 43 | IServiceStackSerializer OffsetSerializer { get; set; } 44 | /// 45 | /// The ServiceStack.Text JSON serializer to use. 46 | /// 47 | IServiceStackSerializer OffsetDateTimeSerializer { get; set; } 48 | /// 49 | /// The ServiceStack.Text JSON serializer to use. 50 | /// 51 | IServiceStackSerializer PeriodSerializer { get; set; } 52 | /// 53 | /// The ServiceStack.Text JSON serializer to use. 54 | /// 55 | IServiceStackSerializer ZonedDateTimeSerializer { get; set; } 56 | /// 57 | /// The in use. 58 | /// 59 | IDateTimeZoneProvider Provider { get; set; } 60 | } 61 | } -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText/IServiceStackSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace NodaTime.Serialization.ServiceStackText 2 | { 3 | /// 4 | /// A JSON serializer for types which can be represented by a single string value, parsed or formatted. 5 | /// 6 | /// The type to convert to/from JSON. 7 | public interface IServiceStackSerializer 8 | { 9 | /// 10 | /// Serializes an object of to JSON. 11 | /// 12 | /// 13 | /// 14 | string Serialize(T value); 15 | /// 16 | /// Deserializes JSON to an object of . 17 | /// 18 | /// 19 | /// 20 | T Deserialize(string text); 21 | /// 22 | /// When true, JsConfig RawSerializeFn and RawDeserializeFn are set. 23 | /// Otherwise SerializeFn and DeSerializeFn are set. 24 | /// 25 | bool UseRawSerializer { get; } 26 | } 27 | } -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText/JsConfigWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ServiceStack.Text; 3 | 4 | namespace NodaTime.Serialization.ServiceStackText 5 | { 6 | public class JsConfigWrapper 7 | { 8 | public static void SetDeserializerMember(Func deserializeFunc) 9 | { 10 | JsConfig.DeSerializeFn = deserializeFunc; 11 | } 12 | 13 | public static void SetRawDeserializerMember(Func deserializeFunc) 14 | { 15 | JsConfig.RawDeserializeFn = deserializeFunc; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText/NodaSerializerDefinitions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NodaTime.Text; 3 | 4 | namespace NodaTime.Serialization.ServiceStackText 5 | { 6 | /// 7 | /// Convenience class to expose preconfigured serializers for Noda Time types, and factory methods 8 | /// for creating those which require parameters. 9 | /// 10 | public static class NodaSerializerDefinitions 11 | { 12 | /// 13 | /// Converter for , using the ISO-8601 date pattern. 14 | /// 15 | public static readonly IServiceStackSerializer AnnualDateSerializer = 16 | new StandardServiceStackSerializer( 17 | AnnualDatePattern.Iso, 18 | ServiceStackFallbackDeserializers.ToAnnualDate 19 | ); 20 | 21 | /// 22 | /// Converter for . This uses the same serializer as the default Instant Serializer. 23 | /// 24 | public static IServiceStackSerializer ComplexIntervalSerializer = new ComplexJsonIntervalSerializer(CreateInstantSerializer()); 25 | 26 | /// 27 | /// Converter for . This uses the same serializer as the default Instant Serializer. 28 | /// 29 | public static IServiceStackSerializer ExtendedIsoIntervalSerializer = new ExtendedIsoIntervalSerializer(CreateInstantSerializer()); 30 | 31 | /// 32 | /// Converter for , using the ISO-8601 time pattern, extended as required to accommodate milliseconds and ticks. 33 | /// 34 | public static IServiceStackSerializer LocalTimeSerializer = 35 | new StandardServiceStackSerializer( 36 | LocalTimePattern.ExtendedIso, 37 | ServiceStackFallbackDeserializers.ToLocalTime); 38 | 39 | /// 40 | /// Converter for , using the ISO-8601 date pattern. 41 | /// 42 | public static readonly IServiceStackSerializer LocalDateSerializer = 43 | new StandardServiceStackSerializer( 44 | LocalDatePattern.Iso, 45 | ServiceStackFallbackDeserializers.ToLocalDate, 46 | CreateIsoValidator(x => x.Calendar)); 47 | 48 | /// 49 | /// Converter for , using the ISO-8601 date/time pattern, extended as required to accommodate milliseconds and ticks. 50 | /// No time zone designator is applied. 51 | /// 52 | public static readonly IServiceStackSerializer LocalDateTimeSerializer = 53 | new StandardServiceStackSerializer( 54 | LocalDateTimePattern.ExtendedIso, 55 | ServiceStackFallbackDeserializers.ToLocalDateTime, 56 | CreateIsoValidator(x => x.Calendar)); 57 | 58 | /// 59 | /// Converter for . 60 | /// 61 | public static readonly IServiceStackSerializer OffsetDateTimeSerializer = 62 | new StandardServiceStackSerializer( 63 | OffsetDateTimePattern.ExtendedIso, 64 | ServiceStackFallbackDeserializers.ToOffsetDateTime, 65 | CreateIsoValidator(x => x.Calendar)); 66 | 67 | /// 68 | /// Converter for , using the ISO-8601 date/time pattern, extended as required to accommodate milliseconds and ticks, and 69 | /// specifying 'Z' at the end to show it's effectively in UTC. 70 | /// 71 | public static readonly IServiceStackSerializer InstantSerializer = CreateInstantSerializer(); 72 | 73 | /// 74 | /// Creates a serializer for zoned date/times, using the given . 75 | /// 76 | /// The to use when parsing. 77 | /// The text pattern for parsing. 78 | /// A serializer to handle . 79 | public static IServiceStackSerializer CreateZonedDateTimeSerializer(IDateTimeZoneProvider provider, string pattern = "yyyy'-'MM'-'dd'T'HH':'mm':'ss;FFFFFFFo z") 80 | { 81 | return new StandardServiceStackSerializer( 82 | ZonedDateTimePattern.CreateWithInvariantCulture(pattern, provider), 83 | ServiceStackFallbackDeserializers.ToZonedDateTime, 84 | CreateIsoValidator(x => x.Calendar)); 85 | } 86 | 87 | /// 88 | /// Round-tripping converter for . Use this when you really want to preserve information, 89 | /// and don't need interoperability with systems expecting ISO. 90 | /// 91 | public static IServiceStackSerializer RoundtripPeriodSerializer = 92 | new StandardServiceStackSerializer(PeriodPattern.Roundtrip); 93 | 94 | /// 95 | /// Normalizing ISO converter for . Use this when you want compatibility with systems expecting 96 | /// ISO durations (~= Noda Time periods). However, note that Noda Time can have negative periods. Note that 97 | /// this converter losses information - after serialization and deserialization, "90 minutes" will become "an hour and 30 minutes". 98 | /// 99 | public static IServiceStackSerializer NormalizingIsoPeriodSerializer = 100 | new StandardServiceStackSerializer(PeriodPattern.NormalizingIso); 101 | 102 | /// 103 | /// Converter for . 104 | /// 105 | public static IServiceStackSerializer DurationSerializer = 106 | new StandardServiceStackSerializer(DurationPattern.CreateWithInvariantCulture("-H:mm:ss.FFFFFFF")); 107 | 108 | /// 109 | /// Creates a serializer for time zones, using the given . 110 | /// 111 | /// The to use when parsing. 112 | /// A serializer to handle . 113 | public static IServiceStackSerializer CreateDateTimeZoneSerializer(IDateTimeZoneProvider provider) 114 | { 115 | return new DateTimeZoneSerializer(provider); 116 | } 117 | 118 | /// 119 | /// Converter for . 120 | /// 121 | public static IServiceStackSerializer OffsetSerializer = 122 | new StandardServiceStackSerializer(OffsetPattern.GeneralInvariant); 123 | 124 | private static IServiceStackSerializer CreateInstantSerializer() 125 | { 126 | return new StandardServiceStackSerializer( 127 | InstantPattern.ExtendedIso, 128 | ServiceStackFallbackDeserializers.ToInstant); 129 | } 130 | 131 | private static Action CreateIsoValidator(Func calendarProjection) 132 | { 133 | return value => 134 | { 135 | var calendar = calendarProjection(value); 136 | // We rely on CalendarSystem.Iso being a singleton here. 137 | if (calendar != CalendarSystem.Iso) 138 | { 139 | throw new ArgumentException( 140 | string.Format("Values of type {0} must (currently) use the ISO calendar in order to be serialized.", 141 | typeof(T).Name)); 142 | } 143 | }; 144 | } 145 | } 146 | } -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText/NodaTime.Serialization.ServiceStackText.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;net45 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText/ServiceStackFallbackDeserializers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | using ServiceStack.Text; 4 | 5 | namespace NodaTime.Serialization.ServiceStackText 6 | { 7 | /// 8 | /// Standard ServiceStack.Text JSON fallback deserializers for NodaTime. These are used by 9 | /// to make deserialization more resilient and forgiving to misbehaving consumers. 10 | /// 11 | public static class ServiceStackFallbackDeserializers 12 | { 13 | /// 14 | /// Attempts to generate a by deserializing to a first. 15 | /// 16 | /// The JSON to deserialize. 17 | /// The deserialized 18 | /// Failed to deserialize to a 19 | public static AnnualDate ToAnnualDate(string text) 20 | { 21 | var dateTime = DeserializeStruct(text); 22 | return new AnnualDate(dateTime.Month, dateTime.Day); 23 | } 24 | 25 | /// 26 | /// Attempts to generate a by deserializing to a first. 27 | /// 28 | /// The JSON to deserialize. 29 | /// The deserialized 30 | /// Failed to deserialize to a 31 | public static Instant ToInstant(string text) 32 | { 33 | var dateTimeOffset = DeserializeStruct(text); 34 | var instant = Instant.FromDateTimeOffset(dateTimeOffset); 35 | return instant; 36 | } 37 | 38 | /// 39 | /// Attempts to generate a by deserializing to a first. 40 | /// 41 | /// The JSON to deserialize. 42 | /// The deserialized 43 | /// Failed to deserialize to a 44 | public static LocalTime ToLocalTime(string text) 45 | { 46 | var dateTime = DeserializeStruct(text); 47 | var localTime = LocalTime.FromTicksSinceMidnight(dateTime.TimeOfDay.Ticks); 48 | return localTime; 49 | } 50 | 51 | /// 52 | /// Attempts to generate a by deserializing to a first. 53 | /// 54 | /// The JSON to deserialize. 55 | /// The deserialized 56 | /// Failed to deserialize to a 57 | public static LocalDate ToLocalDate(string text) 58 | { 59 | var dateTimeOffset = DeserializeStruct(text); 60 | var localDate = OffsetDateTime.FromDateTimeOffset(dateTimeOffset).Date; 61 | return localDate; 62 | } 63 | 64 | /// 65 | /// Attempts to generate a by deserializing to a first. 66 | /// 67 | /// The JSON to deserialize. 68 | /// The deserialized 69 | /// Failed to deserialize to a 70 | public static LocalDateTime ToLocalDateTime(string text) 71 | { 72 | var dateTimeOffset = DeserializeStruct(text); 73 | var localDateTime = OffsetDateTime.FromDateTimeOffset(dateTimeOffset).LocalDateTime; 74 | return localDateTime; 75 | } 76 | 77 | /// 78 | /// Attempts to generate a by deserializing to a first. 79 | /// 80 | /// The JSON to deserialize. 81 | /// The deserialized 82 | /// Failed to deserialize to a 83 | public static OffsetDateTime ToOffsetDateTime(string text) 84 | { 85 | var dateTimeOffset = DeserializeStruct(text); 86 | var offsetDateTime = OffsetDateTime.FromDateTimeOffset(dateTimeOffset); 87 | return offsetDateTime; 88 | } 89 | 90 | /// 91 | /// Attempts to generate a by deserializing to a first. 92 | /// 93 | /// The JSON to deserialize. 94 | /// The deserialized 95 | /// Failed to deserialize to a 96 | public static ZonedDateTime ToZonedDateTime(string text) 97 | { 98 | var dateTimeOffset = DeserializeStruct(text); 99 | var zonedDateTime = ZonedDateTime.FromDateTimeOffset(dateTimeOffset); 100 | return zonedDateTime; 101 | } 102 | 103 | private static T DeserializeStruct(string text) where T : struct 104 | { 105 | var deserializedType = JsonSerializer.DeserializeFromString(text); 106 | 107 | if (deserializedType == null) 108 | { 109 | throw new SerializationException(string.Format("Unable to deserialize to {0}.", typeof (T).Name)); 110 | } 111 | 112 | return deserializedType.Value; 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /src/NodaTime.Serialization.ServiceStackText/StandardServiceStackSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | using NodaTime.Text; 4 | 5 | namespace NodaTime.Serialization.ServiceStackText 6 | { 7 | /// 8 | /// A JSON serializer for types which can be represented by a single string value, parsed or formatted 9 | /// from an . 10 | /// 11 | /// The type to convert to/from JSON. 12 | public class StandardServiceStackSerializer : IServiceStackSerializer 13 | { 14 | private readonly IPattern _pattern; 15 | 16 | private readonly Func _serviceStackFallbackDeSerializer; 17 | 18 | private readonly Action _serializationValidator; 19 | 20 | /// 21 | /// does not use the ServiceStack.Text raw serializer. 22 | /// 23 | public bool UseRawSerializer 24 | { 25 | get { return false; } 26 | } 27 | 28 | /// 29 | /// Creates a new instance with a pattern and an optional validator and/or fallback deserializer. 30 | /// The validator will be called before each value is written, and may throw an exception to indicate 31 | /// that the value cannot be serialized. The fallback serializer will be called when parsing using the 32 | /// pattern fails. 33 | /// 34 | /// The pattern to use for parsing and formatting. 35 | /// 36 | /// The validator to call before writing values. May be null, indicating that no validation is required. 37 | /// is null. 38 | public StandardServiceStackSerializer( 39 | IPattern pattern, 40 | Func serviceStackFallbackDeSerializer = null, 41 | Action serializationValidator = null) 42 | { 43 | if (pattern == null) 44 | { 45 | throw new ArgumentNullException("pattern"); 46 | } 47 | this._pattern = pattern; 48 | this._serializationValidator = serializationValidator; 49 | this._serviceStackFallbackDeSerializer = serviceStackFallbackDeSerializer; 50 | } 51 | 52 | /// 53 | /// Serializes the given object. 54 | /// 55 | /// The object to serialize. 56 | /// The serialized representation. 57 | public string Serialize(T value) 58 | { 59 | if (this._serializationValidator != null) 60 | { 61 | this._serializationValidator(value); 62 | } 63 | 64 | return _pattern.Format(value); 65 | } 66 | 67 | /// 68 | /// Deserializes the given JSON. 69 | /// 70 | /// The JSON to parse. 71 | /// The deserialized object. 72 | public T Deserialize(string text) 73 | { 74 | var parsedResult = _pattern.Parse(text); 75 | 76 | if (parsedResult.Success) 77 | { 78 | return parsedResult.Value; 79 | } 80 | 81 | if (_serviceStackFallbackDeSerializer == null) 82 | { 83 | throw parsedResult.Exception; 84 | } 85 | 86 | T fallbackObj; 87 | 88 | try 89 | { 90 | fallbackObj = _serviceStackFallbackDeSerializer(text); 91 | } 92 | catch (SerializationException) 93 | { 94 | //If fallback fails, throw original exception. 95 | throw parsedResult.Exception; 96 | } 97 | 98 | return fallbackObj; 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", 3 | "version": "3.1", 4 | "publicReleaseRefSpec": [ 5 | "^refs/heads/master$", 6 | "^refs/heads/v\\d+(?:\\.\\d+)?$" 7 | ], 8 | "cloudBuild": { 9 | "buildNumber": { 10 | "enabled": true 11 | } 12 | } 13 | } --------------------------------------------------------------------------------