0

I am working with an XML api, which returns the following:

<inventory>
    <product inventoryId="1722474"  externalReference="SM" site="global" total="0" allocated="0" available="0" frozen="0" onOrder="0" lastStockChangeId="505401" lastLineRequirementChangeId="0"/>
    <product inventoryId="1722476"  externalReference="PM" site="global" total="0" allocated="0" available="0" frozen="0" onOrder="0" lastStockChangeId="243256" lastLineRequirementChangeId="0"/>
    .... 1000s of nodes ....
</inventory>

So, from this returned xml nodes, I am only interested in the following fields/attributes externalReference and available.

Therefore; I created the following class to describe the xml content I am going to deserialize/parse:

[XmlRoot(ElementName = "product")]
public class StockLevelProduct
{
    [XmlAttribute(AttributeName = "externalReference")]
    public string ExternalReference { get; set; }

    [XmlAttribute(AttributeName = "available")]
    public string Available { get; set; }
}

[XmlRoot(ElementName = "inventory")]
public class StockLevelResult
{
    [XmlElement(ElementName = "product")]
    public List<StockLevelProduct> Product { get; set; }
}

Then I put it all together like this:

// Init
StockLevelResult stockLevelResult;

// Anticipate errors
try
{
    // Generate request url
    string requestUrl = string.Format("{0}/remotewarehouse/inventory.xml?channel={1}",
        apiUrl,
        apiChannel);

    // Call api
    string apiResultXmlString = ApiGet(requestUrl);

    // Fix api result xml string
    if (!apiResultXmlString.Contains("<?xml"))
        apiResultXmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + apiResultXmlString;

    // Deserialize xml string to object
    stockLevelResult = XmlParser.Parse<StockLevelResult>(apiResultXmlString);
}
catch (Exception ex)
{
    Console.WriteLine("Failed to download stock levels - " + ex.Message);
}

Note* the returned xml string from the API server does not contain <?xml version="1.0" encoding="UTF-8"?> so I manually add it. Note sure if XmlParser.Parse requires it.

When this code executes; I get the following exception being thrown:

There is an error in XML document (1871, 60)

Any ideas why this isn't working? Is it an issue with the returned XML string? or the way I am trying to parse/deserialize it?

Latheesan
  • 23,247
  • 32
  • 107
  • 201
  • 1
    1) What does the XML look like around line 1871 and character 60? 2) What is `XmlParser.Parse(string)`? Is that a wrapper around [`XmlSerializer`](https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer%28v=vs.110%29.aspx)? If so, `XmlSerializer` doesn't require the `` line. 3) What is the full `ToString()` output of the exception including the message, type and traceback? – dbc Mar 23 '16 at 16:30
  • Line 1871 had this: `` so this `externalReference="V02002B&R"` had an ampersand which was causing the error. I found a fix for this type of issue, will be posting it as answer; see blow. Thanks for helping me find this. – Latheesan Mar 24 '16 at 09:01

2 Answers2

0

Try this

XmlSerializer xs = new XmlSerializer(typeof(StockLevelResult));
StringReader sReader = new StringReader(apiResultXmlString);
XmlTextReader reader = new XmlTextReader(sReader);
stockLevelResult = (StockLevelResult)xs.Deserialize(reader);
jdweng
  • 33,250
  • 2
  • 15
  • 20
0

The exception was caused by bad data in the api XML response:

<product inventoryId="1726460" externalReference="V02002B&R" site="global" total="0" allocated="0" available="0" frozen="0" onOrder="0" lastStockChangeId="76231" lastLineRequirementChangeId="0"/>

i.e. & in this attribute externalReference="V02002B&R"

I fixed it thanks to this answer like this:

// Call api
string apiResultXmlString = ApiGet(requestUrl);

// Fix api result xml string
if (!apiResultXmlString.Contains("<?xml"))
    apiResultXmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + apiResultXmlString;

// Fix bad response data
string pattern = "(?<start>>)(?<content>.+?(?<!>))(?<end><)|(?<start>\")(?<content>.+?)(?<end>\")";
apiResultXmlString = System.Text.RegularExpressions.Regex.Replace(apiResultXmlString, pattern, m =>
            m.Groups["start"].Value +
            System.Web.HttpUtility.HtmlEncode(System.Web.HttpUtility.HtmlDecode(m.Groups["content"].Value)) +
            m.Groups["end"].Value);
Community
  • 1
  • 1
Latheesan
  • 23,247
  • 32
  • 107
  • 201