Visual Studio 2019, C# .NET Core 3.1, no 3rd party libraries
I am consuming the USPS Address Validation web service which sends its reply in XML. However, the service will actually send one of two different replies, depending on whether or not it encounters an error. For example, a normal reply looks like this:
<AddressValidateResponse>
<Address ID="0">
<Address1> STE K</Address1>
<Address2>29851 AVENTURA</Address2>
<City>RANCHO SANTA MARGARITA</City>
<CityAbbreviation>RCHO STA MARG</CityAbbreviation>
<State>CA</State>
<Zip5>92688</Zip5>
<Zip4>2014</Zip4>
<DeliveryPoint>83</DeliveryPoint>
<CarrierRoute>C057</CarrierRoute>
<Footnotes>N</Footnotes>
<DPVConfirmation>Y</DPVConfirmation>
<DPVCMRA>N</DPVCMRA>
<DPVFootnotes>AABB</DPVFootnotes>
<Business>Y</Business>
<CentralDeliveryPoint>N</CentralDeliveryPoint>
<Vacant>N</Vacant>
</Address>
</AddressValidateResponse>
However, if the service encounters an error, it will return the following XML:
<Error>
<Number>12345</Number>
<Source>USPS.WEB.API</Source>
<Description>Error encountered</Description>
</Error>
Unfortunately in both instances the http status code is 200 for a successful http request, even though the service itself generated an error.
I'm not sure how to handle conditional deserialization of this scenario. I wrote a routine which deserializes the regular response into a C# class, however, if I receive the ERROR reply, then routine chokes with an "unexpected XML" error when it attempts to deserialize into the AddressValidateResponse class.
Is there a way to consolidate both of these replies into a single class and tell the deserializer to, essentially, "ignore" the root element name when it deserializes. In other words, could I create a class (abbreviated for example purposes like this:
public class MyClass
{
public Int32 Number {get; set;} = 0;
public String Source {get; set;} = "USPS";
...
public Address Address {get; set;} = null;
}
and then deserialize either the ERROR or ADDRESSVALIDATERESPONSE resultant XML into this 'umbrella' class, where I would get the error information if the service throws an error, but get the correct address information (with a default 0 error number) if the response is successful?
I attempted this on my end, but all I get are XML deserialization errors, because (in my example) the root element name in the XML response from USPS is either ERROR or ADDRESSVALIDATERESPONSE and that doesn't match the XMLRoot element name of my custom class.
Since there doesn't seem to be a way to allow for multiple possible root element names in deserialization, is there a way to ignore the name and have the deserializer populate the children elements?
Or, is there a way to have an umbrella class, like:
public class MyClass
{
public Error Error { get; set; } = null;
public AddressValidateResponse AddressValidateResponse { get; set; } = null
}
and then have the deserializer populate either the error or addressvalidateresponse sub-classes depending on the root element tag in the response?
I suppose I can look at the web response string for the presence of either ERROR or ADDRESSVALIDATERESPONSE and write code to conditionally serialize into a temporary class which I then copy into the umbrella class, but that seems like a hack to me.
Wondering what other solutions folks may think of, or if there's a way to natively have this scenario handled without writing a lot of conditional code. Unfortunately this is the way the API response, which is poor design IMO (other APIs like UPS and Fedex encapsulate error information into a single response which will contain nulls for other fields when there's an error, but for some reason USPS insists on sending one of two different replies.
I assume the response you get is a string, load the string as an XDocument. Wrap your XML deserialization code -
The XDocument is within the System.Xml.Linq namespace
To load a string -