Proxy/Wrapper class for a dynamic object from a SOAP API
up vote
6
down vote
favorite
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
BasketResponse
in the constructor and set the values of the properties whenbasket
is 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.
c# xml wrapper soap
add a comment |
up vote
6
down vote
favorite
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
BasketResponse
in the constructor and set the values of the properties whenbasket
is 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.
c# xml wrapper soap
2
I'm struggling to find a reason for usingdynamic
here. Can you post the code from theSoapSimplifier
constructor? Maybe I'm missing something about what exactly it's trying to achieve.
– Flater
Oct 31 '17 at 16:21
Well @Flater, you asked for it. I said it was horrible!
– Jason Elkin
Oct 31 '17 at 21:18
add a comment |
up vote
6
down vote
favorite
up vote
6
down vote
favorite
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
BasketResponse
in the constructor and set the values of the properties whenbasket
is 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.
c# xml wrapper soap
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
BasketResponse
in the constructor and set the values of the properties whenbasket
is 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.
c# xml wrapper soap
c# xml wrapper soap
edited Nov 1 '17 at 9:05
asked Oct 31 '17 at 12:13
Jason Elkin
1314
1314
2
I'm struggling to find a reason for usingdynamic
here. Can you post the code from theSoapSimplifier
constructor? Maybe I'm missing something about what exactly it's trying to achieve.
– Flater
Oct 31 '17 at 16:21
Well @Flater, you asked for it. I said it was horrible!
– Jason Elkin
Oct 31 '17 at 21:18
add a comment |
2
I'm struggling to find a reason for usingdynamic
here. Can you post the code from theSoapSimplifier
constructor? Maybe I'm missing something about what exactly it's trying to achieve.
– Flater
Oct 31 '17 at 16:21
Well @Flater, you asked for it. I said it was horrible!
– Jason Elkin
Oct 31 '17 at 21:18
2
2
I'm struggling to find a reason for using
dynamic
here. Can you post the code from the SoapSimplifier
constructor? Maybe I'm missing something about what exactly it's trying to achieve.– Flater
Oct 31 '17 at 16:21
I'm struggling to find a reason for using
dynamic
here. Can you post the code from the SoapSimplifier
constructor? Maybe I'm missing something about what exactly it's trying to achieve.– Flater
Oct 31 '17 at 16:21
Well @Flater, you asked for it. I said it was horrible!
– Jason Elkin
Oct 31 '17 at 21:18
Well @Flater, you asked for it. I said it was horrible!
– Jason Elkin
Oct 31 '17 at 21:18
add a comment |
1 Answer
1
active
oldest
votes
up vote
0
down vote
The answer to your second question...
Would it be better to interrogate the dynamic BasketResponse in the constructor and set the values of the properties when basket is instantiated?
Is yes.
You're fetching the data, so set it in the constructor, that way, you're doing the work in one place.
Is there a need to use the BasketResponse Property again. Aren't you mapping all the data you need already?
Yes, I need to keep hold ofBasketResponse
. My instinct was to set values in the constructor but that means that I am duping the data. My (limited) understanding of dynamics suggests that this would also force the compiler to evaluate the dynamic objects/properties when the class is constructed, whether or not a property is actually used. Wrapping them in the getter makes them lazy which seems more efficient.
– Jason Elkin
Nov 1 '17 at 8:55
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
0
down vote
The answer to your second question...
Would it be better to interrogate the dynamic BasketResponse in the constructor and set the values of the properties when basket is instantiated?
Is yes.
You're fetching the data, so set it in the constructor, that way, you're doing the work in one place.
Is there a need to use the BasketResponse Property again. Aren't you mapping all the data you need already?
Yes, I need to keep hold ofBasketResponse
. My instinct was to set values in the constructor but that means that I am duping the data. My (limited) understanding of dynamics suggests that this would also force the compiler to evaluate the dynamic objects/properties when the class is constructed, whether or not a property is actually used. Wrapping them in the getter makes them lazy which seems more efficient.
– Jason Elkin
Nov 1 '17 at 8:55
add a comment |
up vote
0
down vote
The answer to your second question...
Would it be better to interrogate the dynamic BasketResponse in the constructor and set the values of the properties when basket is instantiated?
Is yes.
You're fetching the data, so set it in the constructor, that way, you're doing the work in one place.
Is there a need to use the BasketResponse Property again. Aren't you mapping all the data you need already?
Yes, I need to keep hold ofBasketResponse
. My instinct was to set values in the constructor but that means that I am duping the data. My (limited) understanding of dynamics suggests that this would also force the compiler to evaluate the dynamic objects/properties when the class is constructed, whether or not a property is actually used. Wrapping them in the getter makes them lazy which seems more efficient.
– Jason Elkin
Nov 1 '17 at 8:55
add a comment |
up vote
0
down vote
up vote
0
down vote
The answer to your second question...
Would it be better to interrogate the dynamic BasketResponse in the constructor and set the values of the properties when basket is instantiated?
Is yes.
You're fetching the data, so set it in the constructor, that way, you're doing the work in one place.
Is there a need to use the BasketResponse Property again. Aren't you mapping all the data you need already?
The answer to your second question...
Would it be better to interrogate the dynamic BasketResponse in the constructor and set the values of the properties when basket is instantiated?
Is yes.
You're fetching the data, so set it in the constructor, that way, you're doing the work in one place.
Is there a need to use the BasketResponse Property again. Aren't you mapping all the data you need already?
answered Oct 31 '17 at 21:42
mrdnk
1265
1265
Yes, I need to keep hold ofBasketResponse
. My instinct was to set values in the constructor but that means that I am duping the data. My (limited) understanding of dynamics suggests that this would also force the compiler to evaluate the dynamic objects/properties when the class is constructed, whether or not a property is actually used. Wrapping them in the getter makes them lazy which seems more efficient.
– Jason Elkin
Nov 1 '17 at 8:55
add a comment |
Yes, I need to keep hold ofBasketResponse
. My instinct was to set values in the constructor but that means that I am duping the data. My (limited) understanding of dynamics suggests that this would also force the compiler to evaluate the dynamic objects/properties when the class is constructed, whether or not a property is actually used. Wrapping them in the getter makes them lazy which seems more efficient.
– Jason Elkin
Nov 1 '17 at 8:55
Yes, I need to keep hold of
BasketResponse
. My instinct was to set values in the constructor but that means that I am duping the data. My (limited) understanding of dynamics suggests that this would also force the compiler to evaluate the dynamic objects/properties when the class is constructed, whether or not a property is actually used. Wrapping them in the getter makes them lazy which seems more efficient.– Jason Elkin
Nov 1 '17 at 8:55
Yes, I need to keep hold of
BasketResponse
. My instinct was to set values in the constructor but that means that I am duping the data. My (limited) understanding of dynamics suggests that this would also force the compiler to evaluate the dynamic objects/properties when the class is constructed, whether or not a property is actually used. Wrapping them in the getter makes them lazy which seems more efficient.– Jason Elkin
Nov 1 '17 at 8:55
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f179252%2fproxy-wrapper-class-for-a-dynamic-object-from-a-soap-api%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2
I'm struggling to find a reason for using
dynamic
here. Can you post the code from theSoapSimplifier
constructor? Maybe I'm missing something about what exactly it's trying to achieve.– Flater
Oct 31 '17 at 16:21
Well @Flater, you asked for it. I said it was horrible!
– Jason Elkin
Oct 31 '17 at 21:18