I have to interface with a horrible PHP SOAP API that returns nested arrays of strings. I've written a helper that can parse the SOAP response and return a dynamic object but I need at least some properties to be strongly typed.
The API returns a lot more data that I need to keep up front (but need to interrogate later) and apart from a few guaranteed fields, it is truly dynamic (it could contain pretty much anything).
I have created a proxy class that wraps the dynamic object like so:
public class Basket
{
public Basket(string basketResponse, BasketToken token)
{
this.Token = token;
this.BasketResponse = new SoapSimplifier(basketResponse).ToJObject();
}
public string FirstName { get { return this.BasketResponse.deliverydata.firstname; } }
public string LastName { get { return this.BasketResponse.deliverydata.lastname; } }
public string Tel { get { return this.BasketResponse.deliverydata.mobile; } }
public string Email { get { return this.BasketResponse.deliverydata.email; } }
public string Address1 { get { return this.BasketResponse.deliverydata.add1; } }
public string PostCode { get { return this.BasketResponse.deliverydata.postcode; } }
public BasketToken Token { get; }
public dynamic BasketResponse { get; }
}
The BasketResponse contains more data that I will to interrogate later on (with a shed-load of TryParse,Try/Catch and Linq) so I do need to keep it. I also need to store it in a database - fortunately Cosmos DB can handle dynamics.
My questions are:
- Is this a sensible approach, using the dynamic object like one might use backing fields?
- Would it be better to interrogate the dynamic
BasketResponsein the constructor and set the values of the properties whenbasketis instantiated?
I'm especially concerned about performance.
May be relevant:
Here is the SoapSimplifier
internal class SoapSimplifier
{
private static XNamespace Xsi = "http://www.w3.org/2001/XMLSchema-instance";
private XElement ParsedResponse { get; set; }
private JObject ParsedJObject { get; set; }
public SoapSimplifier(string responseString)
{
this.ParsedResponse = XDocument.Parse(responseString).Descendants("return").First();
}
public JObject ToJObject()
{
if(this.ParsedJObject == null)
{
ParsedJObject = this.XElementToJObject(ParsedResponse);
}
return ParsedJObject;
}
public override string ToString()
{
return this.ToJObject().ToString();
}
private JObject XElementToJObject(XElement Item)
{
JObject jo = new JObject();
foreach (var i in Item.Elements("item"))
{
var valNode = i.Element("value");
if (valNode != null)
{
var nodeType = valNode.Attribute(Xsi + "type").Value;
if (nodeType == "xsd:string")
{
jo.Add(new JProperty(i.Element("key").Value, i.Element("value").Value));
}
else if (nodeType == "ns2:Map")
{
jo.Add(new JProperty(i.Element("key").Value, XElementToJObject(valNode)));
}
else if (nodeType == "SOAP-ENC:Array")
{
JArray ja = new JArray();
foreach (var node in valNode.Elements())
{
ja.Add(XElementToJObject(node));
}
jo.Add(new JProperty(i.Element("key").Value, ja));
}
}
}
return jo;
}
}
This takes the SOAP response generated from a PHP array sent via SOAP (similar to this one) and parses it into a JObject.
This is SOAP Jim, but not as we know it. The API provider will not supply a WSDL. Apart from a few guaranteed keys the array(s) returned could contain any number of key/value pairs and or other arrays.
As PHP arrays are dynamic, I can't think of a way of representing them in C# that isn't also dynamic.
I'm using jObject because it's fast and I can simultaneously create a readable JSON string for logging and a dynamic object that can be queried in c# with dot syntax.
dynamichere. Can you post the code from theSoapSimplifierconstructor? Maybe I'm missing something about what exactly it's trying to achieve. \$\endgroup\$