Parse XML That Has Namespaces
Your company just went live with the FreightEx carrier network, and their gateway pushes a shipment status message — an XML document — after every package scan.
The previous developer's parser worked perfectly against the sandbox samples; then production traffic arrived and every field started coming back empty: no errors, no crashes, just nothing found. The difference is one attribute at the top of the message — the sandbox samples carried no namespace declarations, production messages do, and the FreightEx spec says they are entirely within their rights to.
The message
A typical production message:
<?xml version="1.0" encoding="UTF-8"?>
<ShipmentStatus xmlns="urn:tryal:freight:shipping:v2">
<Header>
<ShipmentNo>SHP-10412</ShipmentNo>
</Header>
<Packages>
<Package>
<trk:TrackingNo xmlns:trk="urn:tryal:freight:tracking:v1">1Z-00443-7</trk:TrackingNo>
<Weight unit="kg">12.5</Weight>
</Package>
<Package>
<trk:TrackingNo xmlns:trk="urn:tryal:freight:tracking:v1">1Z-00443-8</trk:TrackingNo>
<Weight unit="kg">3.25</Weight>
</Package>
</Packages>
</ShipmentStatus>What the FreightEx spec guarantees — and nothing more:
- The elements
ShipmentStatus,Header,ShipmentNo,Packages,PackageandWeightbelong to the namespaceurn:tryal:freight:shipping:v2. - The element
TrackingNobelongs to the namespaceurn:tryal:freight:tracking:v1. - Namespace prefixes are the sender's choice and change between messages: either namespace may appear under any prefix, or as a default (
xmlns="...") declaration, on any element. Only the two URIs above carry stable identity. - The
unitattribute onWeightnever carries a prefix. - Messages may also carry elements from other partners' namespaces — sometimes with the very same local names (
ShipmentNo,Package,TrackingNo) — anywhere in the document, including insidePackages. They are not yours and must be ignored. - Shipping-namespace
Packageelements appear only insidePackages;HeaderandPackageseach appear at most once, directly under the root. - Every message is well-formed XML, and element content is plain text.
Requirements
Create a codeunit named "Shipment Status Parser" with four public procedures:
procedure GetShipmentNo(XmlPayload: Text): Text
procedure CountPackages(XmlPayload: Text): Integer
procedure GetTrackingNumbers(XmlPayload: Text): List of [Text]
procedure GetWeightUnit(XmlPayload: Text): TextRules:
GetShipmentNoreturns the text content of the shipping-namespaceShipmentNoelement insideHeader;''if the message has none.CountPackagesreturns how many shipping-namespacePackageelements the message contains;0for an empty shipment.GetTrackingNumbersreturns the text content of every tracking-namespaceTrackingNoelement, in document order; an empty list when there are none.GetWeightUnitreturns the value of the firstunitattribute found on a shipping-namespaceWeightelement, in document order;''when noWeightcarries one (or there is noWeightat all).- Elements with the right local name but the wrong namespace never count, are never returned, and never mask the real ones.
- None of the procedures may raise an error on a message that satisfies the spec above.
What the tests check
The grading tests feed your parser generated messages: with the shipping namespace declared as default and declared under a prefix, with the tracking namespace under changing prefixes (including a default declaration placed directly on TrackingNo), with foreign-namespace ShipmentNo/Package/TrackingNo/Weight decoys under the root and inside Packages (including a foreign Weight carrying its own unit attribute ahead of the real one), with a unit-less shipping Weight placed before one that carries the unit, and with shipment numbers, counts, tracking numbers and units randomized so pattern-matching the sample above cannot pass. Comparisons and counts are exact — over-matching a decoy fails just as hard as finding nothing. The empty cases are graded too: a missing ShipmentNo, an empty shipment, tracking-free packages and a Weight without a unit must come back as '', 0 or an empty list — never as an error.