I have the following response from a SOAP message. I need to read the values from the REQUEST_STATUS tag and the RESULT tag and put it in a datatable to update the table inside my database.
Please help me as I am doing this for the first time.
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<CalculationResponse xmlns="http://www.someurl.com/">
<OUTPUT>
<REQUEST_STATUS>
<IS_SUCCESS>true</IS_SUCCESS>
<IS_PARTIAL_SUCCESS>true</IS_PARTIAL_SUCCESS>
</REQUEST_STATUS>
<RESULT>
<Value>
<inset1>1<inset1>
</Value>
<Value>
<inset2>2<inset2>
</Value>
</RESULT>....
C# Code I tried:
XDocument doc = XDocument.Load(Environment.CurrentDirectory + "\\out.xml");
XNamespace ns = #"http://www.someurl.com/";
bool validation = false;
var root = doc.Descendants("CalculationResponse").Elements("OUTPUT").Elements("REQUEST_STATUS");
var valuestr = from r in root.Elements("IS_SUCCESS")
select r.Value;
validation = (valuestr.ToString()=="true")?true:false;
I always get the valuestr has no results in the collection.
Here is the code I made it to achieve:
C# Code:
XNamespace ns = #"http://www.someurl.com";
bool validation = false;
var parent = doc.Root.Descendants(ns + "CalculationResponse").Descendants(ns + "OUTPUT");
var valuestr = (from r in parent.Descendants(ns + "REQUEST_STATUS").Descendants(ns + "IS_SUCCESS")
select r.Value).ToList();
validation = (valuestr[0] == "true") ? true : false;
Related
My Web API returns a string looking like XML file below. I put all rows ending with \r\n to new line for readability.
{"DataTable.RemotingVersion":{"major":2,"minor":0,"build":-1,"revision":-1,"majorRevision":-1,"minorRevision":-1},"XmlSchema":" <?xml version=\"1.0\" encoding=\"utf-16\"?>\r\n
<xs:schema xmlns=\"\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\r\n
<xs:element name=\"Test\">\r\n
<xs:complexType>\r\n
<xs:sequence>\r\n
<xs:element name=\"Col1\" type=\"xs:string\" msdata:targetNamespace=\"\" minOccurs=\"0\" />\r\n
<xs:element name=\"Col2\" type=\"xs:string\" msdata:targetNamespace=\"\" minOccurs=\"0\" />\r\n
</xs:sequence>\r\n
</xs:complexType>\r\n
</xs:element>\r\n
<xs:element name=\"tmpDataSet\" msdata:IsDataSet=\"true\" msdata:MainDataTable=\"Test\" msdata:UseCurrentLocale=\"true\">\r\n
<xs:complexType>\r\n
<xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\" />\r\n
</xs:complexType>\r\n
</xs:element>\r\n
</xs:schema>","XmlDiffGram":"<diffgr:diffgram xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\" xmlns:diffgr=\"urn:schemas-microsoft-com:xml-diffgram-v1\">\r\n
<tmpDataSet>\r\n
<Test diffgr:id=\"Test1\" msdata:rowOrder=\"0\">\r\n
<Col1>Column 1 value</Col1>\r\n
<Col2>Column 2 value</Col2>\r\n
</Test>\r\n
</tmpDataSet>\r\n
</diffgr:diffgram>"}
I wonder how I can feed this string to a datatable? I tried to feed the table like this
System.Data.DataTable dt = new System.Data.DataTable();
byte[] byteArray =Encoding.ASCII.GetBytes(<above string returned by Web API>);
MemoryStream stream = new MemoryStream( byteArray );
dt.ReadXml(stream);
However, the code failed with exception
System.Xml.XmlException: 'Data at the root level is invalid, position 1
Actually, your WebAPI didn't respond with a valid XML payload but return a JSON instead :
{
"DataTable.RemotingVersion": {...},
"XmlSchema":"... xml schema string ...",
"XmlDiffGram": " ... xml data string ..."
}
So you can't read the whole string by dt.ReadXml(stream);
Also note that the xml string begins with an empty string. It's better to invoke .Trim() before reading the XML string.
A working demo:
Let's create two helper methods that read the schema & data parts into DataTable:
// read the schema into DataTable
public static void ReadSchema(DataTable dt,string schema)
{
var stream = new MemoryStream();
ReadXMLToMemoryStream(schema, stream);
dt.ReadXmlSchema(stream);
}
// read the data into DataTable
public static void ReadData(DataTable dt,string xml)
{
var stream = new MemoryStream();
ReadXMLToMemoryStream(xml,stream);
dt.ReadXml(stream);
}
// read xml string into MemoryStream
private static void ReadXMLToMemoryStream(string xml, MemoryStream stream)
{
var doc = new XmlDocument();
doc.LoadXml(xml.Trim());
doc.Save(stream);
stream.Position = 0;
}
To fix the issue, we need parse the related json properties (I'm using Newtonsoft.Json) :
byte[] bytes = Encoding.ASCII.GetBytes(strReturnedFromWebApi);
MemoryStream ms = new MemoryStream(bytes);
using(var reader = new JsonTextReader(new StreamReader(ms))){
var json = JToken.Load(reader);
var version= json.SelectToken("$.['DataTable.RemotingVersion']");
var schema = (string)json.SelectToken("$.XmlSchema"); // the schema part
var data = (string)json.SelectToken("$.XmlDiffGram"); // the data part
System.Data.DataTable dt = new System.Data.DataTable();
ReadSchema(dt,schema); // read schema
ReadData(dt,data); // read data
}
I am using the IPP .NET SDK (ver 2.0.1.0), VS2013, and C#
My code to submit a new item is as follows (I've plugged in static data to verify).
var item = new Item()
{
Type = ItemTypeEnum.Inventory,
TrackQtyOnHand = true,
QtyOnHand = 12,
InvStartDate = DateTime.Today,
Active = true,
Name = "TEST0004",
Description = "Test Product",
PurchaseDesc = "Test Product",
Taxable = true,
SalesTaxIncluded = false,
IncomeAccountRef = new ReferenceType() { Value = "1" }, // SALES
ExpenseAccountRef = new ReferenceType() { Value = "50" }, // COGS
AssetAccountRef = new ReferenceType() { Value = "49" }, // Inventory Asset
UnitPrice = 10M,
PurchaseCost = 7.5M,
sparse = true
};
ServiceContext serviceContext = getServiceContext(profile);
var data = new DataService(serviceContext).Add<Item>(item);
My expectation is to havea new inventory item added with quantity tracking enabled. Below is the response I get when querying for this product using the API Explorer.
<IntuitResponse xmlns="http://schema.intuit.com/finance/v3" time="2013-12-30T11:45:02.640-08:00">
<QueryResponse startPosition="1" maxResults="1">
<Item domain="QBO" sparse="false">
<Id>21</Id>
<SyncToken>0</SyncToken>
<MetaData>
<CreateTime>2013-12-30T11:40:41-08:00</CreateTime>
<LastUpdatedTime>2013-12-30T11:40:41-08:00</LastUpdatedTime>
</MetaData>
<Name>TEST0004</Name>
<Description>Test Product</Description>
<Active>true</Active>
<FullyQualifiedName>TEST0004</FullyQualifiedName>
<Taxable>false</Taxable>
<UnitPrice>0</UnitPrice>
<Type>Service</Type>
<IncomeAccountRef name="Sales">1</IncomeAccountRef>
<PurchaseDesc>Test Product</PurchaseDesc>
<PurchaseCost>0</PurchaseCost>
<ExpenseAccountRef name="Cost of Goods Sold">50</ExpenseAccountRef>
<TrackQtyOnHand>false</TrackQtyOnHand>
</Item>
</QueryResponse>
</IntuitResponse>
The most critical problems I'm encountering are:
1. The item gets added as a Service type not Inventory
2. The TrackQtyOnHand is false, not true
3. The AssetAccountRef is missing,
4. The initial QtyOnHand is missin,
5. The PurchaseCost and UnitPrice are 0
What's going on? Is this SDK broken? Or am I missing something. I've been able to get very similar code working for locations (departments), categories (classes).
Thanks in advance for any help you can provide.
Please check if you have enabled "QtyOnHand" from QBO's Preference menu.
I've tried this from APIExplorer. It works fine there. Please have a look.
Create Request
<Item xmlns="http://schema.intuit.com/finance/v3" sparse="false">
<Name>ABCD_CLONE</Name>
<Active>true</Active>
<FullyQualifiedName>ABCD</FullyQualifiedName>
<Taxable>false</Taxable>
<UnitPrice>45</UnitPrice>
<Type>Inventory</Type>
<IncomeAccountRef name="Sales of Product Income">49</IncomeAccountRef>
<PurchaseCost>0</PurchaseCost>
<ExpenseAccountRef name="Cost of Goods Sold">50</ExpenseAccountRef>
<AssetAccountRef name="Inventory Asset">51</AssetAccountRef>
<TrackQtyOnHand>true</TrackQtyOnHand>
<QtyOnHand>1000</QtyOnHand>
<InvStartDate>2013-12-31</InvStartDate>
</Item>
Response
<IntuitResponse xmlns="http://schema.intuit.com/finance/v3" time="2013-12-30T19:42:45.781-08:00">
<Item domain="QBO" sparse="false">
<Id>6</Id>
<SyncToken>0</SyncToken>
<MetaData>
<CreateTime>2013-12-30T19:42:45-08:00</CreateTime>
<LastUpdatedTime>2013-12-30T19:42:45-08:00</LastUpdatedTime>
</MetaData>
<Name>ABCD_CLONE</Name>
<Active>true</Active>
<FullyQualifiedName>ABCD_CLONE</FullyQualifiedName>
<Taxable>false</Taxable>
<UnitPrice>45</UnitPrice>
<Type>Inventory</Type>
<IncomeAccountRef name="Sales of Product Income">49</IncomeAccountRef>
<PurchaseCost>0</PurchaseCost>
<ExpenseAccountRef name="Cost of Goods Sold">50</ExpenseAccountRef>
<AssetAccountRef name="Inventory Asset">51</AssetAccountRef>
<TrackQtyOnHand>true</TrackQtyOnHand>
<QtyOnHand>1000</QtyOnHand>
<InvStartDate>2013-12-31</InvStartDate>
</Item>
</IntuitResponse>
If possible please grab the original request XML from code by configuring the logger in debug mode.
PN - There is an open bug related to QtyOnHand update. That is expected to be fixed in the next release.
Thanks
The main reason that your C# code does not work is because Intuit requires that you set various "Is Specified" properties, such as TrackQtyOnHandSpecified = true.
Hopefully they will improve their property setting to automatically set these Specified fields to true when we assign data to them. For now, you will need to look at each property you set to see if there is an associated Specified property to set at the same time. Awkward for now.
Cheers,
Greg
It seems they still didn't fix this issue. But here I am, I can tell u a workaround to fix this issue.
Just create a new Item as you usually do :
Item i = new Item();
i.Name = "Art 1";
i.TrackQtyOnHand = true;
i.TypeSpecified = true;
i.Type = ItemTypeEnum.Inventory;
i.QtyOnHand = 0;
i.InvStartDate = DateTime.Now;
i.PurchaseCost = 10;
i.UnitPrice = 111;
i.FullyQualifiedName = "Art 1";
i.IncomeAccountRef = new ReferenceType
{
Value = "63"
};
i.ExpenseAccountRef = new ReferenceType
{
Value = "64"
};
i.AssetAccountRef = new ReferenceType
{
Value = "65",
name = "Inventory Asset"
};
Item newItem = dataService.Add(i) as Item;
Now to fix this you must make immediately an update to the newly added product as so:
newItem.UnitPrice = i.UnitPrice;
newItem.PurchaseCost = i.PurchaseCost;
newItem.QtyOnHand = i.QtyOnHand;
newItem.InvStartDate = i.InvStartDate;
newItem.Type = i.Type;
newItem.TypeSpecified = true;
newItem.AssetAccountRef = i.AssetAccountRef;
i = dataService.Update(newItem) as Item;
And wala, that's it.
Hope it will be helpful to somebody ;)
Does anyone know how to calculate the MD5 hash that is needed to be used with Amazon's SubmitFeed API? I am using ColdFusion and every time I calculate the MD5 hash on my end it never matches what Amazon calculates.
Amazon responds with this error:
ContentMD5DoesNotMatch
the Content-MD5 HTTP header you passed for your feed (C7EF1CADB27497B46FCD6F69516F96E0) did not match the Content-MD5 we calculated for your feed (x+8crbJ0l7RvzW9pUW+W4A==)
I am using the built-in function that ColdFusion uses for hashing (example hash(myStr)). Is there a step I am missing?
public any function EncryptSignature(required string argValue,required string publicKey) hint="I create my own signature that I will matching later." {
local.filters=StructNew();
local.filters["F:publicKey"]=arguments.publicKey;
var jMsg=JavaCast("string",arguments.argValue).getBytes("iso-8859-1");
var thisSecretKey = getDAO().getSecretKey(local.filters).apiSecretKey;
var jKey=JavaCast("string",thisSecretKey).getBytes("iso-8859-1");
var key=createObject("java","javax.crypto.spec.SecretKeySpec");
var mac=createObject("java","javax.crypto.Mac");
key=key.init(jKey,"HmacSHA1");
mac=mac.getInstance(key.getAlgorithm());
mac.init(key);
mac.update(jMsg);
return lCase(binaryEncode(mac.doFinal(),'Hex'));
//return Encrypt(arguments.argValue,getapiUsersDAO().getSecretKey(arguments.publicKey),'HMAC-SHA1');
}
The problem is that Feed must have preceding
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
Even adding it as a Declaration is not enough
var memoryStream = new MemoryStream();
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("AmazonEnvelope",
...
if you are using XmlWriter:
using (var xmlWriter = XmlWriter.Create(memoryStream))
{
doc.WriteTo(xmlWriter);
}
You have to save XDocument to file and then get stream from file. Only in this case XDocument preserves declaration (designed behaviour of Save() and WriteTo() methods):
var memoryStream = new MemoryStream();
doc.Save(memoryStream);
var file = Path.GetTempFileName();
using (var fileStream = File.OpenWrite(file))
{
var buffer = memoryStream.GetBuffer();
fileStream.Write(buffer, 0, (int)memoryStream.Length);
}
return File.Open(file, FileMode.Open, FileAccess.Read);
I checked this online tool and you just need to send that MD5 in base64 encoding. It is currently just hexadecimal encoded.
I'm afraid I don't know what the ColdFusion way to do that is, maybe this:
SHA or MD5 Digests in ColdFusion
Here is what I did to get this to work:
<cfset getMD5 = ToBase64(binaryDecode(hash(xmlRequest),'hex'))>
And bang it matched Amazons MD5 hash.
Here is an alternative java-way
found on http://www.kba.suche-spezialwerkzeug.de/pdf/MWSDeveloperGuide.pdf
public static String computeContentMD5HeaderValue(FileInputStream fis)
throws IOException, NoSuchAlgorithmException {
DigestInputStream dis = new DigestInputStream(fis,
MessageDigest.getInstance("MD5"));
byte[] buffer = new byte[8192];
while (dis.read(buffer) > 0)
;
String md5Content = new String(
org.apache.commons.codec.binary.Base64.encodeBase64(dis.getMessageDigest().digest())
);
// Effectively resets the stream to be beginning of the file via a
// FileChannel.
fis.getChannel().position(0);
return md5Content;
}
This will give your desire output.
<cfset binaryValue = binaryDecode( 'C7EF1CADB27497B46FCD6F69516F96E0', "hex" )>
<cfset base64Value = binaryEncode( binaryValue, "base64" )>
<cfdump var="#base64Value#">
Error Message:
The data reader returned by the store data provider does not have enough columns for the query requested.
public ObjectResult<global::System.String> P_GET_MST_CODE(global::System.String i_RES_TYPE, ObjectParameter v_RESULT)
{
ObjectParameter i_RES_TYPEParameter;
if (i_RES_TYPE != null)
{
i_RES_TYPEParameter = new ObjectParameter("I_RES_TYPE", i_RES_TYPE);
}
else
{
i_RES_TYPEParameter = new ObjectParameter("I_RES_TYPE", typeof(global::System.String));
}
return base.ExecuteFunction<global::System.String>("P_GET_MST_CODE", i_RES_TYPEParameter, v_RESULT);
}
Below is the definition of the stored procedure.
<Function Name="P_GET_MST_CODE" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="LEGACY">
<Parameter Name="I_RES_TYPE" Type="varchar2" Mode="In" />
<Parameter Name="V_RESULT" Type="varchar2" Mode="Out" />
</Function>
Can anyone help me to solve this problem?
I've solved this by avoiding entity connection object. :-)
Until now, It seems to be unsupported for OUT Parameter of ORACLE Database.
Below is the changed code example.
using System.Data;
using System.Data.Common;
using System.Data.EntityClient;
using System.ServiceModel.DomainServices.EntityFramework;
using System.ServiceModel.DomainServices.Server;
using Oracle.DataAccess.Client;
.......
[Invoke]
public string method(string OTL)
{
DbCommand cmd = (((EntityConnection)this.ObjectContext.Connection).StoreConnection).CreateCommand();
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.CommandText = "LoveMe";
OracleParameter ep = new OracleParameter("I_RES_TYPE", OTL);
ep.OracleDbType = OracleDbType.Varchar2;
ep.Direction = ParameterDirection.Input;
OracleParameter epV_RESULT = new OracleParameter("V_RESULT", null);
epV_RESULT.OracleDbType = OracleDbType.Varchar2;
epV_RESULT.Size = 30;
epV_RESULT.Direction = ParameterDirection.Output;
cmd.Parameters.Add(ep);
cmd.Parameters.Add(epV_RESULT);
cmd.Connection.Open();
cmd.ExecuteNonQuery();
string result = cmd.Parameters[1].Value.ToString(); //What I want to get.
cmd.Connection.Close();
return result;
}
I am attempting to use Newtonsoft.Json.Net35 Version 4.0.2.0 to deserialize an ADO.NET DataTable that contains null values. Serialization works fine:
[Test]
public void SerializeDataTableWithNull()
{
var table = new DataTable();
table.Columns.Add("item");
table.Columns.Add("price", typeof(double));
table.Rows.Add("shirt", 49.99);
table.Rows.Add("pants", 54.99);
table.Rows.Add("shoes"); // no price
var json = JsonConvert.SerializeObject(table);
Assert.AreEqual(#"["
+ #"{""item"":""shirt"",""price"":49.99},"
+ #"{""item"":""pants"",""price"":54.99},"
+ #"{""item"":""shoes"",""price"":null}]", json);
}
Deserialization works fine if values are missing:
[Test]
public void DerializeDataTableWithImplicitNull()
{
const string json = #"["
+ #"{""item"":""shirt"",""price"":49.99},"
+ #"{""item"":""pants"",""price"":54.99},"
+ #"{""item"":""shoes""}]";
var table = JsonConvert.DeserializeObject<DataTable>(json);
Assert.AreEqual("shirt", table.Rows[0]["item"]);
Assert.AreEqual("pants", table.Rows[1]["item"]);
Assert.AreEqual("shoes", table.Rows[2]["item"]);
Assert.AreEqual(49.99, (double)table.Rows[0]["price"], 0.01);
Assert.AreEqual(54.99, (double)table.Rows[1]["price"], 0.01);
Assert.IsInstanceOf(typeof(System.DBNull), table.Rows[2]["price"]);
}
If, however, values are explicitly null:
[Test]
public void DerializeDataTableWithExplicitNull()
{
const string json = #"["
+ #"{""item"":""shirt"",""price"":49.99},"
+ #"{""item"":""pants"",""price"":54.99},"
+ #"{""item"":""shoes"",""price"":null}]";
var table = JsonConvert.DeserializeObject<DataTable>(json);
Assert.AreEqual("shirt", table.Rows[0]["item"]);
Assert.AreEqual("pants", table.Rows[1]["item"]);
Assert.AreEqual("shoes", table.Rows[2]["item"]);
Assert.AreEqual(49.99, (double)table.Rows[0]["price"], 0.01);
Assert.AreEqual(54.99, (double)table.Rows[1]["price"], 0.01);
Assert.IsInstanceOf(typeof(System.DBNull), table.Rows[2]["price"]);
}
DeserializeObject throws "System.ArgumentException : Cannot set Column 'price' to be null. Please use DBNull instead." The following workaround works for my particular JSON:
var regex = new Regex(#",?""[_\w]+"":null");
var nullless = regex.Replace(json, string.Empty);
var table = JsonConvert.DeserializeObject<DataTable>(nullless);
but like all regular expression-based kludges this is clearly brittle.
Finally, the questions:
Is this a bug?
Json.NET has many events that can be hooked. Is there a way to get notified when a when a null value is encountered and explicitly set the value to System.DBNull?
Thanks in advance,
Frank
It looks like this is a bug which is easily fixed by replacing
dr[columnName] = reader.Value
with
dr[columnName] = reader.Value ?? System.DBNull.Value
in Newtonsoft.Json.Converters.DataTableConverter. I have entered an issue in the tracker.