SonarQube: Enterprise Edition v10.6 (92116)
Deploy: Unknown, company handles that
SonarScanner .Net: 9.0.0.100868
Okay,
this one is more complicated. Basically, we have a rather integrated system utilizing generic bindingable lists and dictionaries, and other such objects that do not play well with the XmlSerializer in all situations. To combat that we have a CustomSerializer system, implementing IXmlSerializable, that handles many of the edge condition, oddities we need in (de)serializing our objects to and from XML.
One of the advantages of this system, is that polymorphic object hierarchies can be serialized into the same xml, instead of requiring these polymophic classes to be created in duplicate and in separate namespaces.
For example:
[XmlInclude(typeof(PersonClass)]
// Please assume all of the descendants of RecordBase have an XmlInclude, this is a lot of typing
class RecordBase
{
[XmlElement("config")]
public class Config {get;set;}
}
class Person : RecordBase
{
[XmlElement("id")]
public int Id {get;set;}
[XmlElement("name")]
public string Name {get; set;}
[XmlElement("age")]
public int Age {get;set;}
}
class RecordSubsriber : Person
{
[XmlElement("city")]
public string City {get;set;}
[XmlElement("state")]
public string State {get;set;}
}
class RecordFamilyMember : Person
{
[XmlElement("subId")
public Int SubscriberId {get;set;}
[XmlElement("isspouse")]
public bool IsSpouse {get;set;}
[XmlElement("ischild")]
public bool IsChild {get;set;}
}
[XmlRoot("groups")]
public class Groups : RecordBase
{
[XmlElement("organization")]
public string Organization {get;set;}
[XmlArray("members")]
[XmlArrayItem("person")]
public RecordSubscriber[] Subscribers {get;set;}
}
[XmlRoot("family"]
public class Family : RecordBase
{
[XmlElement("subscriber")]
public RecordSubscriber Subscriber {get;set;}
[XmlArray("members")]
[XmlArrayItem("person")]
public RecordFamilyMember[] Dependents {get;set;}
}
Now in this example, we have no control over the xml being produced. One looks like this:
<groups>
<config/>
<organization>PETA</organization>
<members>
<member>
<id>1</id>
<config/>
<name>Jon Deaux</name>
<age>35</age>
<city />
<state />
</member>
<member>
<id>2</id>
<name>Matt Smith</name>
<age>42</age>
<city />
<state />
</member>
</members>
</groups>
While the other looks very similar.
<family>
<config/>
<subscriber>
<id>1</id>
<config/>
<name>Jon Deaux</name>
<age>35</age>
</subscriber>
<members>
<member>
<id>1</id>
<subid>1</subid>
<name>Jane Deaux</name>
<isspouse>true</isspouse>
</member>
<member>
<id>2</id>
<name>Mark Deaux</name>
<ischild>true</true>
</member>
</members>
</groups>
Pardon my paltry example here, but with the nature of these two situations, which are not perfect because i can’t share the actual code that does it somewhat like this, but the default behavior of the XmlSerializer is to over type inform each element within the list. How it naturally process the XmlInclude and the XmlArray and XmlArrayItem attributes causes it to naturally assume during deserialization tha the “Person” is a the base “Person” class not the “RecordSubscriber” or "RecordFamilyMember’. As well, during serialization, it assumes due to the XmlIncludes on the RecordBase, that it has to provide xsi:type information on each element.
Our custom serializer handles these discrepancies. Enter the method in question:
protected bool SetTarget(object targetObject, bool overrideType)
{
try
{
bool isDescendent = true;
if (targetObject != null)
{
if (this.SerializeType != null) isDescendent = this.SerializeType.IsAssignableFrom(targetObject.GetType());
if ((this.SerializeType == null) || overrideType) this.SerializeType = targetObject.GetType();
}
return isDescendent;
}
finally
{
this.SerializeObject = targetObject;
}
}
Utilizing runtime type information, with our custom serializer, it can correctly interpret that even though the array is of a descendent type, and there is no xsi:type atribute to indicate that “person” is the specific type of person in that class (groups, or family respectively) we can effectively turn a suite of SOAP calls, or other APIs into a a full polymorphic library. so calling the method that returns the xml for Groups or the method that returns Family members, the xml is the same, but the object model can be unified. (yes, it is far more indepth than that, we’re talking 20 or 30 methods that get different variabtions of “subscriber” or "members’, etc).
This is all part of a code generated system.
The use of IsAssignableFrom() specifically allows for out custom Serializer to determine if even we are intrisincally thinking to Deserailize a “Person”, is the expected core type, a RecordSubscriber is a descendant of the Person class, and thus perfectly valid to attempt deserialization without the need to de-normalize the object model, have excessive duplicated code in multiple namespaces, where interaction and sharing of base types is no longer possible.
IsAssignableFrom is not a “Type” check as S2219 assumes, it’s a “Dependent implementation check”.
Does this the targetObject implement SerializeType.
Thanks
Jaeden “Sifo Dyas” al’Raec Ruiner
PS I absolutely understand this rule in other situations.
object o = string;
this.DoSomething(o);
public void DoSomething(object o)
{
if (typeof(string).IsAssignableFrom(o)) HandleString((string)o); //ABSOLUTELY NO
}
public void DoSomething(object o)
{
if (o is string s) HandleString(s); // ABSOLUTELY YES
}
the difference is, the aforementioned engine does not always know the descendant type, but needs to check if the object in question does descend from the type defined. That is the exact purpose of IsAssignableFrom. In my situation IsInstanceOfType absolutely cannot be used.