Skip to content

Commit

Permalink
Merge pull request #36 from nsip/release-14
Browse files Browse the repository at this point in the history
Created a custom JSON Formatter which complies with Goessner notation
  • Loading branch information
joerghuber authored Feb 18, 2020
2 parents cf9e725 + 55aac99 commit 551cba2
Show file tree
Hide file tree
Showing 29 changed files with 514 additions and 315 deletions.
9 changes: 5 additions & 4 deletions Code/Sif3Framework/Sif.Framework/Sif.Framework.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@

<PropertyGroup>
<TargetFrameworks>net461;net462;net47;net471;net472;netstandard2.0</TargetFrameworks>
<Version>3.2.1.11</Version>
<AssemblyVersion>3.2.1.11</AssemblyVersion>
<FileVersion>3.2.1.11</FileVersion>
<Version>3.2.1.12</Version>
<AssemblyVersion>3.2.1.12</AssemblyVersion>
<FileVersion>3.2.1.12</FileVersion>
<Authors>Rafidzal Rafiq</Authors>
<Company>Systemic Pty Ltd</Company>
<Product>SIF Framework library</Product>
<Description>Core library of the SIF Framework based on SIF Infrastructure 3.2.1.</Description>
<Copyright>Copyright © Systemic Pty Ltd 2018</Copyright>
<Copyright>Copyright © Systemic Pty Ltd 2020</Copyright>
<PackageLicenseUrl>http://www.apache.org/licenses/LICENSE-2.0</PackageLicenseUrl>
<PackageProjectUrl>https://github.com/nsip/sif3-framework-dotnet</PackageProjectUrl>
<RepositoryUrl>https://github.com/nsip/sif3-framework-dotnet</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<PackageTags>SIF framework</PackageTags>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* Copyright 2020 Systemic Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using Newtonsoft.Json;
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;

namespace Sif.Framework.WebApi.MediaTypeFormatters
{
/// <summary>
/// XML-based MediaTypeFormatter that serialises an object to XML and then JSON, and deserialises JSON to XML and
/// then an object.
/// </summary>
public class XmlToJsonFormatter : XmlMediaTypeFormatter
{
/// <summary>
/// Create an instance of this formatter.
/// </summary>
public XmlToJsonFormatter()
{
SupportedMediaTypes.Clear();
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/json"));
}

/// <summary>
/// <see cref="MediaTypeFormatter.ReadFromStreamAsync(Type, Stream, HttpContent, IFormatterLogger)"/>
/// </summary>
public override Task<object> ReadFromStreamAsync(
Type type,
Stream readStream,
HttpContent content,
IFormatterLogger formatterLogger)
{
if (type == null) throw new ArgumentNullException(nameof(type));

if (readStream == null) throw new ArgumentNullException(nameof(readStream));

object value = null;

using (StreamReader streamReader = new StreamReader(readStream))
{
string json = streamReader.ReadToEnd();

// If there is JSON data, deserialise into an object.
if (!string.IsNullOrWhiteSpace(json))
{
// Convert the JSON string into an XML document.
XmlDocument xmlDocument = JsonConvert.DeserializeXmlNode(json);

using (XmlReader xmlReader = new XmlNodeReader(xmlDocument))
{
// Deserialise the XML document into an object.
XmlSerializer xmlSerializer = (XmlSerializer)GetDeserializer(type, content);
value = xmlSerializer.Deserialize(xmlReader);
}
}
}

return Task.FromResult(value);
}

/// <summary>
/// <see cref="MediaTypeFormatter.WriteToStreamAsync(Type, object, Stream, HttpContent, TransportContext)"/>
/// </summary>
public override Task WriteToStreamAsync(
Type type,
object value,
Stream writeStream,
HttpContent content,
TransportContext transportContext)
{
if (type == null) throw new ArgumentNullException(nameof(type));

if (writeStream == null) throw new ArgumentNullException(nameof(writeStream));

string xml;

using (StringWriter stringWriter = new StringWriter())
{
using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter))
{
// Serialise the object into an XML string.
XmlSerializer xmlSerialiser = (XmlSerializer)GetSerializer(type, value, content);
xmlSerialiser.Serialize(xmlWriter, value);
xml = stringWriter.ToString();
}
}

// Parse the XML string and remove empty elements (defined by the "xsi:nil" element).
XElement xElement = XElement.Parse(xml);
xElement
.Descendants()
.Where(x =>
string.IsNullOrEmpty(x.Value) &&
x.Attributes().Where(y => y.Name.LocalName == "nil" && y.Value == "true").Count() > 0)
.Remove();
xml = xElement.ToString();

// Convert the XML document into a JSON string.
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
string json = JsonConvert.SerializeXmlNode(xmlDocument);

// Write the JSON string to the stream.
byte[] buf = System.Text.Encoding.Default.GetBytes(json);
writeStream.Write(buf, 0, buf.Length);
writeStream.Flush();

return Task.FromResult(writeStream);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
<Version>2.0.8</Version>
</PackageReference>
<PackageReference Include="Sif.Framework">
<Version>3.2.1.11</Version>
<Version>3.2.1.12</Version>
</PackageReference>
<PackageReference Include="Sif.Specification.DataModel.Au">
<Version>3.4.5</Version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@ public static void Register(HttpConfiguration config)
config.Services.Replace(typeof(IHttpControllerSelector), new ServiceProviderHttpControllerSelector(config));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Sif.Framework.Service.Serialisation;
using Sif.Framework.Utils;
using Sif.Framework.WebApi;
using Sif.Framework.WebApi.MediaTypeFormatters;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http.Formatting;
Expand All @@ -16,53 +17,62 @@ public class WebApiApplication : System.Web.HttpApplication
{
private IRegistrationService registrationService;

private void Register()
{
registrationService = RegistrationManager.ProviderRegistrationService;
registrationService.Register();
}

private void Unregister()
{
registrationService.Unregister();
}

protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);

// URL Postfix Extension: Update the configuration to recognise postfix extensions and map known
// extensions to MIME Types. Additional changes to WebApiConfig.cs are required to fully enable this
// feature.
GlobalConfiguration.Configuration.Formatters.JsonFormatter.AddUriPathExtensionMapping("json", "application/json");
GlobalConfiguration.Configuration.Formatters.XmlFormatter.AddUriPathExtensionMapping("xml", "text/xml");

XmlMediaTypeFormatter formatter = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
formatter.UseXmlSerializer = true;
// XML Serialisation: Define the specific XML serialiser to use to ensure that SIF Data Model Objects (as
// defined by the SIF Specification with XML Schema Definitions (XSDs)) are serialised correctly.
XmlMediaTypeFormatter xmlFormatter = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xmlFormatter.UseXmlSerializer = true;

// XML Serialisation: For each SIF Data Model Object used by each SIF Provider, the following entries are
// required to define the root element for each collection object.
XmlRootAttribute submissionsXmlRootAttribute = new XmlRootAttribute("FinancialQuestionnaireSubmissions") { Namespace = SettingsManager.ProviderSettings.DataModelNamespace, IsNullable = false };
ISerialiser<List<FinancialQuestionnaireSubmission>> submissionsSerialiser = SerialiserFactory.GetXmlSerialiser<List<FinancialQuestionnaireSubmission>>(submissionsXmlRootAttribute);
formatter.SetSerializer<List<FinancialQuestionnaireSubmission>>((XmlSerializer)submissionsSerialiser);
xmlFormatter.SetSerializer<List<FinancialQuestionnaireSubmission>>((XmlSerializer)submissionsSerialiser);

XmlRootAttribute schoolInfosXmlRootAttribute = new XmlRootAttribute("SchoolInfos") { Namespace = SettingsManager.ProviderSettings.DataModelNamespace, IsNullable = false };
ISerialiser<List<SchoolInfo>> schoolInfosSerialiser = SerialiserFactory.GetXmlSerialiser<List<SchoolInfo>>(schoolInfosXmlRootAttribute);
formatter.SetSerializer<List<SchoolInfo>>((XmlSerializer)schoolInfosSerialiser);
xmlFormatter.SetSerializer<List<SchoolInfo>>((XmlSerializer)schoolInfosSerialiser);

XmlRootAttribute studentPersonalsXmlRootAttribute = new XmlRootAttribute("StudentPersonals") { Namespace = SettingsManager.ProviderSettings.DataModelNamespace, IsNullable = false };
ISerialiser<List<StudentPersonal>> studentPersonalsSerialiser = SerialiserFactory.GetXmlSerialiser<List<StudentPersonal>>(studentPersonalsXmlRootAttribute);
formatter.SetSerializer<List<StudentPersonal>>((XmlSerializer)studentPersonalsSerialiser);
xmlFormatter.SetSerializer<List<StudentPersonal>>((XmlSerializer)studentPersonalsSerialiser);

XmlRootAttribute studentSchoolEnrollmentsXmlRootAttribute = new XmlRootAttribute("StudentSchoolEnrollments") { Namespace = SettingsManager.ProviderSettings.DataModelNamespace, IsNullable = false };
ISerialiser<List<StudentSchoolEnrollment>> studentSchoolEnrollmentsSerialiser = SerialiserFactory.GetXmlSerialiser<List<StudentSchoolEnrollment>>(studentSchoolEnrollmentsXmlRootAttribute);
formatter.SetSerializer<List<StudentSchoolEnrollment>>((XmlSerializer)studentSchoolEnrollmentsSerialiser);
xmlFormatter.SetSerializer<List<StudentSchoolEnrollment>>((XmlSerializer)studentSchoolEnrollmentsSerialiser);

// Replacement custom JSON formatter (compliant with Goessner notation).
XmlToJsonFormatter xmlToJsonFormatter = new XmlToJsonFormatter
{
UseXmlSerializer = true
};
xmlToJsonFormatter.AddUriPathExtensionMapping("json", "application/json");
xmlToJsonFormatter.SetSerializer<List<FinancialQuestionnaireSubmission>>((XmlSerializer)submissionsSerialiser);
xmlToJsonFormatter.SetSerializer<List<SchoolInfo>>((XmlSerializer)schoolInfosSerialiser);
xmlToJsonFormatter.SetSerializer<List<StudentPersonal>>((XmlSerializer)studentPersonalsSerialiser);
xmlToJsonFormatter.SetSerializer<List<StudentSchoolEnrollment>>((XmlSerializer)studentSchoolEnrollmentsSerialiser);
GlobalConfiguration.Configuration.Formatters.Add(xmlToJsonFormatter);
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.JsonFormatter);

// Alternative 1.
//XmlRootAttribute xmlRootAttribute = new XmlRootAttribute("StudentPersonals") { Namespace = SettingsManager.ProviderSettings.DataModelNamespace, IsNullable = false };
//formatter.SetSerializer<List<StudentPersonal>>(new XmlSerializer(typeof(List<StudentPersonal>), xmlRootAttribute));
//xmlFormatter.SetSerializer<List<StudentPersonal>>(new XmlSerializer(typeof(List<StudentPersonal>), xmlRootAttribute));

// Alternative 2.
//XmlAttributes attributes = new XmlAttributes();
//attributes.XmlRoot = new XmlRootAttribute("StudentPersonals") { Namespace = SettingsManager.ProviderSettings.DataModelNamespace, IsNullable = false };
//XmlAttributeOverrides overrides = new XmlAttributeOverrides();
//overrides.Add(typeof(List<StudentPersonal>), attributes);
//formatter.SetSerializer<List<StudentPersonal>>(new XmlSerializer(typeof(List<StudentPersonal>), overrides));
//xmlFormatter.SetSerializer<List<StudentPersonal>>(new XmlSerializer(typeof(List<StudentPersonal>), overrides));

// Configure global exception loggers for unexpected errors.
GlobalConfiguration.Configuration.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());
Expand All @@ -79,5 +89,23 @@ protected void Application_End(object sender, System.EventArgs e)
Trace.TraceInformation("********** Application_End **********");
Unregister();
}

/// <summary>
/// Register this SIF Provider with the EnvironmentProvider based upon settings defined in the SIF 3.0
/// Framework configuration, e.g. SifFramework.config.
/// </summary>
private void Register()
{
registrationService = RegistrationManager.ProviderRegistrationService;
registrationService.Register();
}

/// <summary>
/// Unregister this SIF Provider from the EnvironmentProvider.
/// </summary>
private void Unregister()
{
registrationService.Unregister();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2018 Systemic Pty Ltd
* Copyright 2020 Systemic Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -77,7 +77,14 @@ private static StudentPersonal CreateBartSimpson()
private static StudentPersonal CreateStudent()
{
NameOfRecordType name = new NameOfRecordType { Type = NameOfRecordTypeType.LGL, FamilyName = RandomNameGenerator.FamilyName, GivenName = RandomNameGenerator.GivenName };
PersonInfoType personInfo = new PersonInfoType { Name = name };

EmailType[] emails = new EmailType[2]
{
new EmailType {Type = AUCodeSetsEmailTypeType.Item01, Value = $"{name.GivenName}01@gmail.com"},
new EmailType {Type = AUCodeSetsEmailTypeType.Item02, Value = $"{name.GivenName}02@gmail.com"}
};

PersonInfoType personInfo = new PersonInfoType { Name = name, EmailList = emails };
StudentPersonal studentPersonal = new StudentPersonal { RefId = Guid.NewGuid().ToString(), LocalId = random.Next(10000, 99999).ToString(), PersonInfo = personInfo };

return studentPersonal;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@
<Reference Include="Remotion.Linq.EagerFetching, Version=2.2.0.0, Culture=neutral, PublicKeyToken=fee00910d6e5f53b, processorArchitecture=MSIL">
<HintPath>..\packages\Remotion.Linq.EagerFetching.2.2.0\lib\net45\Remotion.Linq.EagerFetching.dll</HintPath>
</Reference>
<Reference Include="Sif.Framework, Version=3.2.1.11, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Sif.Framework.3.2.1.11\lib\net461\Sif.Framework.dll</HintPath>
<Reference Include="Sif.Framework, Version=3.2.1.12, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Sif.Framework.3.2.1.12\lib\net461\Sif.Framework.dll</HintPath>
</Reference>
<Reference Include="Sif.Specification.DataModel.Au, Version=3.4.5.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Sif.Specification.DataModel.Au.3.4.5\lib\netstandard2.0\Sif.Specification.DataModel.Au.dll</HintPath>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<package id="NHibernate" version="5.1.3" targetFramework="net461" />
<package id="Remotion.Linq" version="2.2.0" targetFramework="net461" />
<package id="Remotion.Linq.EagerFetching" version="2.2.0" targetFramework="net461" />
<package id="Sif.Framework" version="3.2.1.11" targetFramework="net461" />
<package id="Sif.Framework" version="3.2.1.12" targetFramework="net461" />
<package id="Sif.Specification.DataModel.Au" version="3.4.5" targetFramework="net461" />
<package id="Sif.Specification.Infrastructure" version="3.2.1" targetFramework="net461" />
<package id="slf4net" version="0.1.32.1" targetFramework="net45" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@
<Reference Include="Remotion.Linq.EagerFetching, Version=2.2.0.0, Culture=neutral, PublicKeyToken=fee00910d6e5f53b, processorArchitecture=MSIL">
<HintPath>..\packages\Remotion.Linq.EagerFetching.2.2.0\lib\net45\Remotion.Linq.EagerFetching.dll</HintPath>
</Reference>
<Reference Include="Sif.Framework, Version=3.2.1.11, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Sif.Framework.3.2.1.11\lib\net461\Sif.Framework.dll</HintPath>
<Reference Include="Sif.Framework, Version=3.2.1.12, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Sif.Framework.3.2.1.12\lib\net461\Sif.Framework.dll</HintPath>
</Reference>
<Reference Include="Sif.Specification.DataModel.Au, Version=3.4.5.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Sif.Specification.DataModel.Au.3.4.5\lib\netstandard2.0\Sif.Specification.DataModel.Au.dll</HintPath>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<package id="NHibernate" version="5.1.3" targetFramework="net461" />
<package id="Remotion.Linq" version="2.2.0" targetFramework="net461" />
<package id="Remotion.Linq.EagerFetching" version="2.2.0" targetFramework="net461" />
<package id="Sif.Framework" version="3.2.1.11" targetFramework="net461" />
<package id="Sif.Framework" version="3.2.1.12" targetFramework="net461" />
<package id="Sif.Specification.DataModel.Au" version="3.4.5" targetFramework="net461" />
<package id="Sif.Specification.Infrastructure" version="3.2.1" targetFramework="net461" />
<package id="slf4net" version="0.1.32.1" targetFramework="net45" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,6 @@
<Reference Include="Remotion.Linq.EagerFetching, Version=2.1.0.0, Culture=neutral, PublicKeyToken=fee00910d6e5f53b, processorArchitecture=MSIL">
<HintPath>..\packages\Remotion.Linq.EagerFetching.2.1.0\lib\net45\Remotion.Linq.EagerFetching.dll</HintPath>
</Reference>
<Reference Include="Sif.Framework, Version=3.2.1.11, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Sif.Framework.3.2.1.11\lib\net461\Sif.Framework.dll</HintPath>
</Reference>
<Reference Include="Sif.Specification.DataModel.Au, Version=3.4.5.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Sif.Specification.DataModel.Au.3.4.5\lib\netstandard2.0\Sif.Specification.DataModel.Au.dll</HintPath>
</Reference>
Expand Down
Loading

0 comments on commit 551cba2

Please sign in to comment.