Meddelandeprenumerationer i Azure Service Bus
Översikt
I Svensk Fotbolls tävlingsadministrativa system Fogis emitteras notifieringar internt om att objekt har ändrats. Det kan vara att ett objekt har skapats, ändrats eller raderats.
(Radering är inte så vanligt p.g.a. beroenden i normaliserade underliggande databastabeller. I vissa klasser finns flaggor för makulering.)
Exempel på entiteter som det på detta sätt skickas meddelanden kring är: tävlingar, matcher, matchresultat, matchhändelser, domaruppdrag.
Ändringsmeddelandena skickas internt i systemet via köer som inte är åtkomliga utifrån. Men det finns en process / meddelandepump som plockar upp alla ändringsmeddelanden och avgör om innehållet (ibland efter viss censur) är offentligt, d.v.s. skulle kunna publiceras på våra hemsidor. Om så är fallet så skickas meddelandet vidare till ämnen i Azure service bus. Genom prenumerationer till dessa ämnen kan tredje part således få omedelbar notifiering om dessa meddelanden.
Det publika notifikationsobjekt som då skickas ut innehåller enbart objektet i sitt tillstånd efter ändringen. Förutom metadata, som avhandlas längre ner, så är själva payloaden alltid ett serialiserat objekt av klassen Svenskfotboll.Fogis.Systemmeddelanden.PublikNotifikation.
Tillgängliga entiteter
De klasser / entiteter som kan förekomma i publika meddelanden till prenumerationerna i Azure service bus är:
Domaruppdrag
Lagengagemang
Match
Matchdeltagare
Matchhändelse
Matchlag
Matchlagledare
Matchresultat
Tävling
Här följer korta avsnitt med en beskrivning av respektive entitet:
Domaruppdrag
Ett domaruppdrag är uppdraget för en domare att döma en given match.
När en match skapas så skapas oftast även ett eller flera domaruppdrag, som specificerar domarroll (huvuddomare, assisterande 1, assisterande 2, etc). Från början är domaruppdragen ”tomma”, d.v.s. det är ännu inte bestämt vilken domare som ska få domaruppdraget.
Domaruppdragen kan ha olika statusar och är i vissa fall inte offentliggjorda förrän en viss tid innan matchen ska spelas. Så länge ett domaruppdrag inte är offentliggjort kommer inga meddelanden om det att skickas till prenumerationerna i servicebussen.
Lagengagemang
Ett lagengagemang är förekomsten av ett lag i en viss tävling. Medan ett lag kan ha en livscykel över flera säsonger är alltså lagengemanget ett visst lags deltagande i en viss tävling. Det kan innehålla information om lagengagemangets status (t.ex. huruvida det är anmält, urdraget eller uteslutet), vad det har för kontaktpersoner i just den här tävlingen, med mera.
Lagengagemanget kapslar också in lagets tabellrad. Med andra ord utgör listan med lagengagemang också tävlingens tabell, ifall man sorterar lagengagemangen efter placering.
Match
En match innehåller information om var och när den spelas, vilka lag som deltar, huruvida den är uppskjuten eller inställd, eventuellt slutresultat, ifall dess domarrapport är godkänd, med mera.
Matchdeltagare
En matchdeltagare är en spelare som tagits ut för att representera ett Matchlag i en match.
Den kan innehålla information så som namn på spelaren, tröjnummer och annat man kan tänkas skriva i en spelarförteckning till en match.
Matchhändelse
En matchhändelse är något som inträffar i en match. Det kan vara kopplat till en spelare (mål, gult kort, etc) men kan också vara relaterat enbart till matchens flöde (halvlek startar), eller ett av lagen (straffspark).
Matchlag
Ett matchlag är en platshållare för ett lag som ska delta i en match, antingen som hemmalag eller bortalag.
Oftast refererar det till ett specifikt lagengagemang. Men det kan också referera till ”vinnaren i match med ID NNN”, t.ex. i en cupstege. Matchlaget kan också innehålla information om spelsystem och tidpunkt för när matchlagets spelartrupp ska offentliggöras. Informationen i matchlagen är kopierade till korresponderande egenskaper i den match som de tillhör.
Matchlagledare
En matchlagledare är en lagledare i en match. Matchlagledaren är kopplad till ett matchlag och en person och kvalificeras av en roll (tränare, massör, materialförvaltare). Ifall en person är spelande ledare sätts hen inte upp som matchlagledare utan som matchdeltagare, med en flagga som anger att hen är just spelande ledare.
Matchresultat
En match kan ha noll, ett, eller flera matchresultat.
Matchresultat kan beskriva resultat efter en viss period / halvlek eller vara ett slutresultat. Detta avgörs av engenskapen MatchresultattypID.
Resultatet kan innehålla antal mål för hemma- respektive bortalag, eller helt enkelt indikera walkover-resultat med flaggorna WO, OW eller WW.
Teknikstack
De flesta delar av Fogis är byggda i .NET. Äldre delar är byggda i .NET Framework, vissa nyare delar är byggda i .NET Core eller dess efterföljare.
Kodexempel i denna dokumentation är skrivna i C#.
De Nuget-paket som refereras är kompilerade för .NET Framework 4.8.
Meddelanden som anländer på kön kan givetvis konsumeras i andra språk, och deserialiseras på det vis konsumenten finner bäst.
Klassbibliotek / Nuget-feed
Alla klassdefinitioner för våra databärande klasser (Match, Tavling, Lagengagemang, o.s.v.) finns i klassbiblioteket FogisLib
.
FogisLib har ett historiskt (men onödigt) beroende till klassbiblioteket Svenskfotboll.Interfaces
som också finns i nuget-feeden.
Själva klasserna som används för meddelandeapparaten ligger i klassbiblioteket Svenskfotboll.Fogis.SystemMeddelanden
.
Samtliga tre klassbibliotek finns i vår publika nuget-feed, på adressen:
Meddelanden på prenumerationerna, deserialisering
När ett meddelande anländer på servicebussens prenumeration är det i form av en händelse som i C# / .NET hanteras i en asynkron event handler. Meddelandets Body är en serialiserad representation av en instans av Svenskfotboll.Fogis.SystemMeddelanden.PublikNotifikation
.
Så här ser klassen Svenskfotboll.Fogis.SystemMeddelanden.PublikNotifikation
ut:
/// <summary>
/// Klass som bär en notifikation med publikt offentliggjord information om ett
/// objekt i Fogis.
/// Ett fält identifierar objektet,
/// ett fält indikerar vilken operation som utförts (skapad, ändrad, raderad), /// ett annat innehåller själva objektet.
/// </summary>
public class PublikNotifikation
{
/// <summary>
/// Identifierare för objektet som notifikationen avser.
/// </summary>
public Identifierare Identifierare { get; set; }
/// <summary>
/// Eventuell identifierare för objektets förälder. T.ex. en tävling ifall
/// det aktuella objektet är en match.
/// </summary>
public Identifierare Foraldraidentifierare { get; set; }
/// <summary>
/// Operationen som utförts (skapad, ändrad, raderad)
/// </summary>
public Operation Operation { get; set; }
/// <summary>
/// Objektet som det ser ut efter ändringen.
/// Ifall operationen är <see cref="Operation.Raderad"/> så är objektet
/// null.
/// </summary>
public object Object { get; set; }
/// <summary>
/// Tidpunkt då händelsen uppstod i ursprungssystemet.
/// Kan i undantagsfall vara null, men bör alltid populeras med bästa
/// möjliga uppskattning.
/// </summary>
public DateTimeOffset HandelseTid { get; set; }
}
Koden för klassen Identifierare ut finns i slutet av denna dokumentation. I korthet identifierar den ett objekt med hjälp av dess Objekttyp (klassnamn) och ID.
Genom identifieraren kan man alltså avgöra vilken klass / typ som själva objektet Object är.
Så här ser lite kod ut från ett av våra exempelprojekt, med en event handler som skriver till konsolen:
private async Task ProcessMessagesAsync(ProcessMessageEventArgs args)
{
// Objekttyp är någon sorts Fogis-klass, inklusive namespace.
// T.ex. Svenskfotboll.Fogis.Match
var objekttyp = args.Message.ApplicationProperties[nameof(PublikNotifikation.Identifierare.Objekttyp)].ToString();
// ID, oftast en Int32, men representerad som text
var objektId = args.Message.ApplicationProperties[nameof(PublikNotifikation.Identifierare.ObjektID)].ToString();
// Operation kan vara Skapad (1), Andrad (2) eller Raderad (3).
var operation = (Operation)args.Message.ApplicationProperties[nameof(PublikNotifikation.Operation)];
Console.WriteLine($"Fick meddelande: SequenceNumber:{args.Message.SequenceNumber}, objekttyp: {objekttyp}, objektId: {objektId}, operation: {operation}");
var publikNotifiering = JsonConvert.DeserializeObject<PublikNotifikation>(Encoding.UTF8.GetString(args.Message.Body));
if (objekttyp == typeof(Svenskfotboll.Fogis.Match).ToString())
{
// Konfigurera en JsonSerializerSettings som fungerar för Matchklassen,
// som har ett par knepiga attribut.
// Detta ska naturligtvis egentligen göras i en mer global variabel,
// men görs nu här för att visa hur det görs i anslutning till dess användning.
var jsonResolver = new PropertyRenameAndIgnoreSerializerContractResolver();
jsonResolver.IgnoreProperty(typeof(Match), "Matchlag1OffentliggorMatchtruppDatumTidForXml");
jsonResolver.IgnoreProperty(typeof(Match), "Matchlag2OffentliggorMatchtruppDatumTidForXml");
JsonSerializer matchJsonSerializer = new JsonSerializer();
matchJsonSerializer.ContractResolver = jsonResolver;
// publikNotifiering.Object kommer vara null ifall operationen är "Raderad".
var match = ((JObject)publikNotifiering.Object)?.ToObject<Svenskfotboll.Fogis.Match>(matchJsonSerializer);
if (match != null)
{
Console.WriteLine($"Thread: {Thread.CurrentThread.ManagedThreadId}. Match med MatchNr {match.MatchNr} mellan {match.Lag1Namn} och {match.Lag2Namn}");
}
}
En speciell JsonResolver / JsonSerializer behöver endast användast för meddelanden där objektet är av typen Svenskfotboll.Fogis.Match
eller Svenskfotboll.Fogis.Matchlag
.
Klassen PropertyRenameAndIgnoreSerializerContractResolver
finns återgiven i slutet av denna dokumentation.
Metadata
Förutom att information om objekttyp och objektid finns med som primära egenskaper i PublikNotifikation.Identifierare
så finns dessa egenskaper kopierade som metadata till själva Servicebus-meddelandet. Denna metadata möjliggör tidig filtrering av händelserna, inklusive filtrering redan i Azure av vad som hamnar på prenumerationen för en given prenumerant.
I dokumentationen nedan beskrivs hur metadata läggs till, vilket ger en prenumerant en indikation på hur den sedan ska läsas ut.
I det här fallet är variabeln message
en instans av klassen Azure.Messaging.ServiceBus.ServiceBusMessage
.
Metadata kan hämtas via ServiceBusMessage.UserProperties
, som är ett IDictionary<String,Object>
.
Allmän metadata
// Allmän metadata för samtliga typer av notifikationer
message.ApplicationProperties.Add(nameof(notifikation.Identifierare.Objekttyp), notifikation.Identifierare.Objekttyp);
message.ApplicationProperties.Add(nameof(notifikation.Identifierare.ObjektID), notifikation.Identifierare.ObjektID);
message.ApplicationProperties.Add(nameof(notifikation.Identifierare.ObjektGUID), notifikation.Identifierare.ObjektGUID);
message.ApplicationProperties.Add(nameof(notifikation.HandelseTid), notifikation.HandelseTid);
message.ApplicationProperties.Add(nameof(notifikation.Operation), (int)notifikation.Operation);
Här följer avsnitt för den specifika metadata som finns för respektive objekttyp som en prenumerant skulle kunna få tillgång till.
Metadata för lagengagemang
// Specifik metadata relevant för lagengagemang.
var lagengagemang = (Svenskfotboll.Fogis.Lagengagemang)notifikation.Object;
if (lagengagemang != null)
{
message.UserProperties.Add(nameof(lagengagemang.LagengagemangID), lagengagemang.LagengagemangID);
message.UserProperties.Add(nameof(lagengagemang.FotbollstypID), lagengagemang.FotbollstypID);
message.UserProperties.Add(nameof(lagengagemang.AgsAvForbundID), lagengagemang.AgsAvForbundID);
message.UserProperties.Add(nameof(lagengagemang.TavlingID), lagengagemang.TavlingID);
message.UserProperties.Add(nameof(lagengagemang.TavlingskategoriID), lagengagemang.TavlingskategoriID);
message.UserProperties.Add(nameof(lagengagemang.ForeningID), lagengagemang.ForeningID);
message.UserProperties.Add(nameof(lagengagemang.ForeningForbundID), lagengagemang.ForeningForbundID);
message.UserProperties.Add(nameof(lagengagemang.LagForbundID), lagengagemang.LagForbundID);
}
else if (notifikation.Foraldraidentifierare?.ObjektID > 0)
{
// När vi raderat ett lagengagemang är detta det bästa sättet att identifiera förälder /tävlingskategori).
message.ApplicationProperties.Add("TavlingskategoriID", notifikation.Foraldraidentifierare.ObjektID);
}
Metadata för match
// Specifik metadata relevant för matcher.
var match = (Svenskfotboll.Fogis.Match)notifikation.Object;
if (match != null)
{
message.UserProperties.Add(nameof(match.MatchID), match.MatchID);
message.UserProperties.Add(nameof(match.FotbollstypID), match.FotbollstypID);
message.UserProperties.Add(nameof(match.TavlingID), match.TavlingID);
message.UserProperties.Add(nameof(match.TavlingskategoriID), match.TavlingskategoriID);
message.UserProperties.Add(nameof(match.Lag1ForeningID), match.Lag1ForeningID);
message.UserProperties.Add(nameof(match.Lag2ForeningID), match.Lag2ForeningID);
message.UserProperties.Add(nameof(match.TavlingAgsAvForbundID), match.TavlingAgsAvForbundID);
message.UserProperties.Add(nameof(match.TavlingskategoriAgsAvForbundID), match.TavlingskategoriAgsAvForbundID);
}
else if (notifikation.Foraldraidentifierare?.ObjektID > 0)
{
// När vi raderat ett matchresultat är detta det bästa sättet att identifiera förälder / match).
message.ApplicationProperties.Add("MatchID", notifikation.Foraldraidentifierare.ObjektID);
}
Metadata för matchdeltagare
// Specifik metadata relevant för matchdeltagare.
var matchdeltagare = (Svenskfotboll.Fogis.Matchdeltagare)notifikation.Object;
if (matchdeltagare != null)
{
message.ApplicationProperties.Add(nameof(matchdeltagare.MatchID), matchdeltagare.MatchID);
message.ApplicationProperties.Add(nameof(matchdeltagare.TavlingID), matchdeltagare.TavlingID);
message.ApplicationProperties.Add(nameof(matchdeltagare.TavlingAgsAvForbundID), matchdeltagare.TavlingAgsAvForbundID);
message.ApplicationProperties.Add(nameof(matchdeltagare.TavlingskategoriAgsAvForbundID), matchdeltagare.TavlingskategoriAgsAvForbundID);
message.ApplicationProperties.Add(nameof(matchdeltagare.ForeningID), matchdeltagare.ForeningID);
message.ApplicationProperties.Add(nameof(matchdeltagare.FotbollstypID), matchdeltagare.FotbollstypID);
}
else if (notifikation.Foraldraidentifierare?.ObjektID > 0)
{
// När vi raderat en matchdeltagare är detta det bästa sättet att identifiera förälder / match).
message.ApplicationProperties.Add("MatchID", notifikation.Foraldraidentifierare.ObjektID);
}
Metadata för matchhändelse
// Specifik metadata relevant för matchhändelser.
var matchhandelse = (Svenskfotboll.Fogis.Matchhandelse)notifikation.Object;
if (matchhandelse != null)
{
message.ApplicationProperties.Add(nameof(matchhandelse.MatchID), matchhandelse.MatchID);
message.ApplicationProperties.Add(nameof(matchhandelse.TavlingID), matchhandelse.TavlingID);
message.ApplicationProperties.Add(nameof(matchhandelse.MatchhandelsetypID), matchhandelse.MatchhandelsetypID);
}
else if (notifikation.Foraldraidentifierare?.ObjektID > 0)
{
// När vi raderat en matchhändelse är detta det bästa sättet att identifiera förälder / match).
message.ApplicationProperties.Add("MatchID", notifikation.Foraldraidentifierare.ObjektID);
}
Metadata för matchlagledare
// Specifik metadata relevant för matchhändelser.
var matchhandelse = (Svenskfotboll.Fogis.Matchhandelse)notifikation.Object;
if (matchhandelse != null)
{
message.ApplicationProperties.Add(nameof(matchhandelse.MatchID), matchhandelse.MatchID);
message.ApplicationProperties.Add(nameof(matchhandelse.TavlingID), matchhandelse.TavlingID);
message.ApplicationProperties.Add(nameof(matchhandelse.MatchhandelsetypID), matchhandelse.MatchhandelsetypID);
}
else if (notifikation.Foraldraidentifierare?.ObjektID > 0)
{
// När vi raderat en matchhändelse är detta det bästa sättet att identifiera förälder / match).
message.ApplicationProperties.Add("MatchID", notifikation.Foraldraidentifierare.ObjektID);
}
Metadata för matchlag
// Specifik metadata relevant för matchlag.
var matchlag = (Svenskfotboll.Fogis.Matchlag)notifikation.Object;
if (matchlag != null)
{
message.ApplicationProperties.Add(nameof(matchlag.MatchID), matchlag.MatchID);
}
else if (notifikation.Foraldraidentifierare?.ObjektID > 0)
{
// När vi raderat ett matchlag är detta det bästa sättet att identifiera förälder / match).
message.ApplicationProperties.Add("MatchID", notifikation.Foraldraidentifierare.ObjektID);
}
Metadata för matchresultat
// Specifik metadata relevant för matchresultat.
var matchresultat = (Svenskfotboll.Fogis.Matchresultat)notifikation.Object;
if (matchresultat != null)
{
message.UserProperties.Add(nameof(matchresultat.MatchID), matchresultat.MatchID);
message.UserProperties.Add(nameof(matchresultat.ArSlutresultat), matchresultat.ArSlutresultat);
message.UserProperties.Add(nameof(matchresultat.MatchresultattypID), matchresultat.MatchresultattypID);
}
Metadata för tävling
// Specifik metadata relevant för tävling.
var tavling = (Svenskfotboll.Fogis.Tavling)notifikation.Object;
if (tavling != null)
{
message.UserProperties.Add(nameof(tavling.TavlingID), tavling.TavlingID);
message.UserProperties.Add(nameof(tavling.TavlingskategoriID), tavling.TavlingskategoriID);
message.UserProperties.Add(nameof(tavling.FotbollstypID), tavling.FotbollstypID);
message.UserProperties.Add(nameof(tavling.AgsAvForbundID), tavling.AgsAvForbundID);
message.UserProperties.Add(nameof(tavling.TavlingskategoriAgsAvForbundID), tavling.TavlingskategoriAgsAvForbundID);
}
else
{
// När vi raderat en tävling är detta det bästa sättet att identifiera förälder / tävlingskategori).
message.ApplicationProperties.Add("TavlingskategoriID", notifikation.Foraldraidentifierare.ObjektID);
}
Övrigt
Meddelanden i testmiljön lever i 3 dygn. Sedan gallras de automatiskt bort.
I produktionsmiljön gallrar vi helst meddelanden efter 8 timmar, men kan anpassa tiden efter behov, ifall så avtalas.
Exempel (i C# / .Net) på hur man ansluter till prenumerationen, tar emot meddelanden, deserialiserar dem och behandlar dem finns i en exempel-lösning som kan tillhandahållas till nya prenumeranter. Det är en lösning som kan öppnas och köras i Microsoft Visual Studio.
För att i ett utvecklings- och testskede få ut händelser till prenumerationerna måste ändringar göras på tävling / match / matchresultat / domaruppdrag / lagengagemang i vår testmiljö.
En testledare hos Svenska Fotbollförbundet kan hjälpa prenumeranter att skapa sådana händelser.
Klassdefinitionerna som finns i FogisLib är alltså samma som vi används internt i Fogis, det tävlingsadministrativa systemet.
Datamodellen i de web-apier som tillhandahålls via Azure API Management är en begränsad mappning av dessa klasser. Tyvärr använder web-apierna engelska begrepp, och alltså inte samma svenska begreppsapparat som inne i Fogis. Mappningen mellan de två modellerna är dock någorlunda lätt att lista ut.
Credentials för anslutning till Azure Service Bus, liksom namn på prenumerationer, kan man få av Svenska Fotbollförbundets IT-avdelning när det finns ett överenskommet affärsavtal.
Appendix: Svenskfotboll.Fogis.Systemmeddelanden.Identifierare
/// <summary>
/// Objekt-identifierare
/// </summary>
public class Identifierare
{
/// <summary>
/// Konstruktor för objekt vars ID är av typen int.
/// </summary>
/// <param name="objekttyp">Fullt kvalificerat klassnamn för objektet,
/// t.ex. Svenskfotboll.Fogis.Match</param>
/// <param name="objektID">Objektets ID, om det är lagrat som int i Fogis.
/// För vissa objekttyper rör vi oss mot att använda Guid.
/// Det är möjligt att det finns värden i både ObjektID och ObjektGUID, men
/// det måste finnas ett värde i åtminstone ett av dem.</param>
public Identifierare(string objekttyp, int objektID) : this(objekttyp)
{
ObjektID = objektID;
}
/// <summary>
/// Konstruktor för objekt vars ID är av typen Guid.
/// </summary>
/// <param name="objekttyp">Fullt kvalificerat klassnamn för objektet,
/// t.ex. Svenskfotboll.Fogis.Match</param>
/// <param name="objektGUID">
/// Objektets ID, om det är lagrat som Guid i Fogis.
/// Det är möjligt att det finns värden i både ObjektID och ObjektGUID, men
/// det måste finnas ett värde i åtminstone ett av dem.
/// </param>
public Identifierare(string objekttyp, string objektGUID) : this(objekttyp)
{
ObjektGUID = objektGUID;
}
private Identifierare(string objekttyp) : this()
{
if (string.IsNullOrEmpty(objekttyp))
{
throw new ArgumentException("Argumentet objekttyp är null eller tomt", nameof(objekttyp));
}
Objekttyp = objekttyp;
}
/// <summary>
/// Parameterlös konstruktor som finns för att underlätta serialisering.
/// </summary>
internal Identifierare()
{
}
/// <summary>
/// Fullt kvalificerat klassnamn för objektet, t.ex.
/// Svenskfotboll.Fogis.Match
/// </summary>
public string Objekttyp { get; set; }
/// <summary>
/// Objektets ID, om det är lagrat som int i Fogis.
/// För vissa objekttyper rör vi oss mot att använda Guid.
/// Det är möjligt att det finns värden i både ObjektID och ObjektGUID, men
/// det måste finnas ett värde i åtminstone ett av dem.
/// </summary>
public int ObjektID { get; set; }
/// <summary>
/// Objektets ID, om det är lagrat som Guid i Fogis.
/// Det är möjligt att det finns värden i både ObjektID och ObjektGUID, men
/// det måste finnas ett värde i åtminstone ett av dem.
/// </summary>
public string ObjektGUID { get; set; }
}
Appendix: PropertyRenameAndIgnoreSerializerContractResolver
/// <summary>
/// Klass som gör det möjligt för oss att dynamiskt ignorera vissa fält vid
/// deserialisering av JSON, även om klassens egenskaper inte är dekorerade
/// med JsonIgnore-direktiv.
/// </summary>
public class **PropertyRenameAndIgnoreSerializerContractResolver** : DefaultContractResolver
{
private readonly Dictionary<Type, HashSet<string>> _ignores;
private readonly Dictionary<Type, Dictionary<string, string>> _renames;
public PropertyRenameAndIgnoreSerializerContractResolver()
{
_ignores = new Dictionary<Type, HashSet<string>>();
_renames = new Dictionary<Type, Dictionary<string, string>>();
}
public void IgnoreProperty(Type type, params string[] jsonPropertyNames)
{
if (!_ignores.ContainsKey(type))
_ignores[type] = new HashSet<string>();
foreach (var prop in jsonPropertyNames)
_ignores[type].Add(prop);
}
public void RenameProperty(Type type, string propertyName, string newJsonPropertyName)
{
if (!_renames.ContainsKey(type))
_renames[type] = new Dictionary<string, string>();
_renames[type][propertyName] = newJsonPropertyName;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (IsIgnored(property.DeclaringType, property.PropertyName))
{
property.ShouldSerialize = i => false;
property.Ignored = true;
}
if (IsRenamed(property.DeclaringType, property.PropertyName, out var newJsonPropertyName))
property.PropertyName = newJsonPropertyName;
return property;
}
private bool IsIgnored(Type type, string jsonPropertyName)
{
if (!ignores.ContainsKey(type))
return false;
return _ignores[type].Contains(jsonPropertyName);
}
private bool IsRenamed(Type type, string jsonPropertyName, out string newJsonPropertyName)
{
Dictionary<string, string> renames;
if (!_renames.TryGetValue(type, out renames) || !renames.TryGetValue(jsonPropertyName, out newJsonPropertyName))
{
newJsonPropertyName = null;
return false;
}
return true;
}
}