Can you simplify XML Arrays? - arrays

I want to create an XML "database" that currently looks like this:
<?xml version="1.0"?>
<ArrayOfSport xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<sport name="football">
<countries>
<country name="england">
<leagues>
<league name="premierleague">
<teams>
<team key="man" value="Manchester United" />
<team key="arse" value="Arsenal" />
<team key="bour" value="Bournemouth" />
</teams>
</league>
</leagues>
</country>
</countries>
</sport>
</ArrayOfSport>
As you can see, this XML can be very messy. So, my question is, can you simplify XML Arrays (<countries>, <leagues>, <teams>) like in the example above into something like this?:
<?xml version="1.0"?>
<ArrayOfSport xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<sport name="football">
<country name="england">
<league name="premierleague">
<team key="man" value="Manchester United" />
<team key="arse" value="Arsenal" />
<team key="bour" value="Bournemouth" />
</league>
</country>
</sport>
</ArrayOfSport>
Or am I missing something?

Fixed it by using XDocument.
Here's the C# code that generated the valid XML:
var sports =
new XElement("sports",
new XElement("sport", new XAttribute("name", "football"),
new XElement("country", new XAttribute("name", "england"),
new XElement("league", new XAttribute("name", "premierleague"),
new XElement("team", new XAttribute("key", "man"), new XAttribute("value", "Manchester United")),
new XElement("team", new XAttribute("key", "arse"), new XAttribute("value", "Arsenal")),
new XElement("team", new XAttribute("key", "bour"), new XAttribute("value", "Bournemouth"))
)
)
)
);
using (var fileStream = new FileStream(#"C:\XElementTest.xml", FileMode.Create))
using (var writer = new StreamWriter(fileStream))
{
writer.Write(sports);
}
It generated this XML:
<sports>
<sport name="football">
<country name="england">
<league name="premierleague">
<team key="man" value="Manchester United" />
<team key="arse" value="Arsenal" />
<team key="bour" value="Bournemouth" />
</league>
</country>
</sport>
</sports>

Related

Query to extract SOAP 1.1 response from 4 different nodes

I have the below XML:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetBatchResponse xmlns="https://webservices.aba.com/">
<web:GetBatchResult xmlns:web="https://webservices.aba.com/">
<web:Loco>
<web:LocoType>
<web:Errors />
<web:Pos />
<web:Ref />
<web:Episode>
<web:EpisodeType>
<web:EpisodeId>1</web:EpisodeId>
<web:StartTime>2021-02-01T01:41:58</web:StartTime>
<web:EpisodeDataAtStart>
<web:Name>Sam</web:Name>
<web:Height>5.10</web:Height>
<web:Age>26</web:Age>
<web:Salary />
</web:EpisodeDataAtStart>
<web:EpisodeDataAtEnd>
<web:Grade>C</web:Grade>
<web:Salary>500</web:Salary>
</web:EpisodeDataAtEnd>
</web:EpisodeType>
</web:Episode>
<web:EmpUnit>21</web:EmpUnit>
<web:EmpOrg>XE</web:EmpOrg>
</web:LocoType>
<web:LocoType>
<web:Errors />
<web:Pos />
<web:Ref />
<web:Episode>
<web:EpisodeType>
<web:EpisodeId>2</web:EpisodeId>
<web:StartTime>2021-02-02T01:41:58</web:StartTime>
<web:EpisodeDataAtStart>
<web:Name>Emma</web:Name>
<web:Height>5.5</web:Height>
<web:Age>23</web:Age>
<web:Salary />
</web:EpisodeDataAtStart>
<web:EpisodeDataAtEnd>
<web:Grade>B</web:Grade>
<web:Salary>550</web:Salary>
</web:EpisodeDataAtEnd>
</web:EpisodeType>
</web:Episode>
<web:EmpUnit>17</web:EmpUnit>
<web:EmpOrg>XE</web:EmpOrg>
</web:LocoType>
</web:Loco>
<web:EndTimeUTC xsi:nil="true" />
</web:GetBatchResult>
</GetBatchResponse>
</soap:Body>
</soap:Envelope>
This is not a valid xml, just a replica created by me. Please ignore any typing mistakes, if found.
I'm able to extract the data from the 1st and the last node, i.e., I'm able to get EpisodeId, StartTime, EmpUnit and EmpOrg using the below query:
DECLARE #xml XML = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetBatchResponse xmlns="https://webservices.aba.com/">
<web:GetBatchResult xmlns:web="https://webservices.aba.com/">
<web:Loco>
<web:LocoType>
<web:Errors />
<web:Pos />
<web:Ref />
<web:Episode>
<web:EpisodeType>
<web:EpisodeId>1</web:EpisodeId>
<web:StartTime>2021-02-01T01:41:58</web:StartTime>
<web:EpisodeDataAtStart>
<web:Name>Sam</web:Name>
<web:Height>5.10</web:Height>
<web:Age>26</web:Age>
<web:Salary />
</web:EpisodeDataAtStart>
<web:EpisodeDataAtEnd>
<web:Grade>C</web:Grade>
<web:Salary>500</web:Salary>
</web:EpisodeDataAtEnd>
</web:EpisodeType>
</web:Episode>
<web:EmpUnit>21</web:EmpUnit>
<web:EmpOrg>XE</web:EmpOrg>
</web:LocoType>
<web:LocoType>
<web:Errors />
<web:Pos />
<web:Ref />
<web:Episode>
<web:EpisodeType>
<web:EpisodeId>2</web:EpisodeId>
<web:StartTime>2021-02-02T01:41:58</web:StartTime>
<web:EpisodeDataAtStart>
<web:Name>Emma</web:Name>
<web:Height>5.5</web:Height>
<web:Age>23</web:Age>
<web:Salary />
</web:EpisodeDataAtStart>
<web:EpisodeDataAtEnd>
<web:Grade>B</web:Grade>
<web:Salary>550</web:Salary>
</web:EpisodeDataAtEnd>
</web:EpisodeType>
</web:Episode>
<web:EmpUnit>17</web:EmpUnit>
<web:EmpOrg>XE</web:EmpOrg>
</web:LocoType>
</web:Loco>
<web:EndTimeUTC xsi:nil="true" />
</web:GetBatchResult>
</GetBatchResponse>
</soap:Body>
</soap:Envelope>'
WITH XMLNAMESPACES (DEFAULT 'https://webservices.aba.com/', 'http://schemas.xmlsoap.org/soap/envelope/' AS soap, 'https://webservices.aba.com/' AS web)
SELECT
P.PT.value('(./web:EpisodeId/text())[1]','INT') AS INT,
P.PT.value('(./web:StartTime/text())[1]','DATETIME') AS StartTime,
L.LT.value('(./web:EmpUnit/text())[1]','int') AS EmpUnit,
L.LT.value('(./web:EmpOrg/text())[1]','char(2)') AS EmpOrg
FROM #XML.nodes('soap:Envelope/soap:Body/GetBatchResponse/web:GetBatchReult/web:Loco/web:LocoType') L(LT)
CROSS APPLY L.LT.nodes('web:Episode/web:EpisodeType')P(PT);
I also need the values for Name, Height, Age, SalaryAtStart, Grade and SalaryAtEnd
Need some help. Thanks in advance.

Update XML stored in a XML column in SQL Server Update

Set Url value by applying filter based on location-type value from given sample.
<page id="{Page_ID}" name="" type="dat" size="0" sequence="26" originalpagenumber="26" location_type="3" location="">
<content description="" content_format="26" />
<AdditionalInfo>
<Url>https://s3-eu-west-1/2019/may//001063/gvb140/683c82a3-b3f5-49ee-a34e-01859e8e2228.mp3</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>
The XML you've shared is invalid so I've taken the liberty to make it valid in an effort to show you how I might approach this.
I've made the assumption you're looking to update values in a table somewhere in your environment. You can run this example in SSMS.
THIS IS MEANT AS AN EXAMPLE ONLY. DO NOT BLINDLY RUN THIS AGAINST PRODUCTION DATA.
First, I created a mock-up table that holds some page XML data. I assumed that a single XML row could contain multiple page nodes with duplicate location_type values.
DECLARE #Pages TABLE ( page_xml XML );
INSERT INTO #Pages ( page_xml ) VALUES (
'<pages>
<page id="{Page_ID}" name="" type="dat" size="0" sequence="26" originalpagenumber="26" location_type="3" location="">
<content description="" content_format="26" />
<AdditionalInfo>
<Url>https://s3-eu-west-1/2019/may//001063/gvb140/683c82a3-b3f5-49ee-a34e-01859e8e2228.mp3</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>
</page>
<page id="{Page_ID}" name="" type="dat" size="0" sequence="27" originalpagenumber="27" location_type="3" location="">
<content description="" content_format="27" />
<AdditionalInfo>
<Url>https://s3-eu-west-1/2019/may//001063/gvb140/683c82a3-b3f5-49ee-a34e-01859e8e2228.mp3</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>
</page>
<page id="{Page_ID}" name="" type="dat" size="0" sequence="28" originalpagenumber="28" location_type="8" location="">
<content description="" content_format="28" />
<AdditionalInfo>
<Url>https://s3-eu-west-1/2019/may//001063/gvb140/683c82a3-b3f5-49ee-a34e-01859e8e2228.mp3</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>
</page>
</pages>' );
Selecting the current results from #Pages shows the following XML:
<pages>
<page id="{Page_ID}" name="" type="dat" size="0" sequence="26" originalpagenumber="26" location_type="3" location="">
<content description="" content_format="26" />
<AdditionalInfo>
<Url>https://s3-eu-west-1/2019/may//001063/gvb140/683c82a3-b3f5-49ee-a34e-01859e8e2228.mp3</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>
</page>
<page id="{Page_ID}" name="" type="dat" size="0" sequence="27" originalpagenumber="27" location_type="3" location="">
<content description="" content_format="27" />
<AdditionalInfo>
<Url>https://s3-eu-west-1/2019/may//001063/gvb140/683c82a3-b3f5-49ee-a34e-01859e8e2228.mp3</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>
</page>
<page id="{Page_ID}" name="" type="dat" size="0" sequence="28" originalpagenumber="28" location_type="8" location="">
<content description="" content_format="28" />
<AdditionalInfo>
<Url>https://s3-eu-west-1/2019/may//001063/gvb140/683c82a3-b3f5-49ee-a34e-01859e8e2228.mp3</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>
</page>
</pages>
There are two pages with a location_type of 3, and one with a location_type of 8.
Next, I declared a few variables which I then used to modify the XML.
DECLARE #type INT = 3, #url VARCHAR(255) = 'http://www.google.com';
/* Update each Url text for the specified location_type */
WHILE EXISTS ( SELECT * FROM #Pages WHERE page_xml.exist( '//pages/page[#location_type=sql:variable("#type")]/AdditionalInfo/Url[text()!=sql:variable("#url")]' ) = 1 )
UPDATE #Pages
SET
page_xml.modify( '
replace value of (//pages/page[#location_type=sql:variable("#type")]/AdditionalInfo/Url/text()[.!=sql:variable("#url")])[1]
with sql:variable("#url")
' );
After running the update the XML now contains:
<pages>
<page id="{Page_ID}" name="" type="dat" size="0" sequence="26" originalpagenumber="26" location_type="3" location="">
<content description="" content_format="26" />
<AdditionalInfo>
<Url>http://www.google.com</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>
</page>
<page id="{Page_ID}" name="" type="dat" size="0" sequence="27" originalpagenumber="27" location_type="3" location="">
<content description="" content_format="27" />
<AdditionalInfo>
<Url>http://www.google.com</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>
</page>
<page id="{Page_ID}" name="" type="dat" size="0" sequence="28" originalpagenumber="28" location_type="8" location="">
<content description="" content_format="28" />
<AdditionalInfo>
<Url>https://s3-eu-west-1/2019/may//001063/gvb140/683c82a3-b3f5-49ee-a34e-01859e8e2228.mp3</Url>
<Name />
<Encoding>audio</Encoding>
<SecurityType>IBCloud</SecurityType>
</AdditionalInfo>
</page>
</pages>
Using the WHILE EXISTS (... ensures that all Url nodes for the requested location_type are updated. In my example here there are two pages with a value of 3 that are updated, while location_type 8 is left alone.
Basically, what this is doing is updating the Url for any page with the requested location_type to the new #url value.
There are two key pieces here, the first being:
.exist( '//pages/page[#location_type=sql:variable("#type")]/AdditionalInfo/Url[text()!=sql:variable("#url")]' )
Which looks for the requested location_type that doesn't have the new #url value.
And the second:
page_xml.modify( '
replace value of (//pages/page[#location_type=sql:variable("#type")]/AdditionalInfo/Url/text()[.!=sql:variable("#url")])[1]
with sql:variable("#url")
' );
Which modifies (updates) the Url for the location type that has yet to be updated. The two "conditions" allow for a loop (WHILE) that ends when they're no longer met, ensuring that all page nodes for the requested location_type are updated.

MWS submit feed returns InputDataError

I have implemented submit feed API for _POST_ORDER_FULFILLMENT_DATA_ and get feedsubmission result. it returns InputDataError.
request:
<?xml version="1.0" encoding="UTF-8"?>
<AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
<Header>
<DocumentVersion>1.01</DocumentVersion>
<MerchantIdentifier>A1OLX7E3BCNOP2</MerchantIdentifier>
</Header>
<MessageType>OrderFulfillment</MessageType>
<Message>
<MessageID>1</MessageID>
<OrderFulfillment>
<MerchantOrderID>114-7129872-3010653</MerchantOrderID>
<MerchantFulfillmentID>5e79aa3205f4f</MerchantFulfillmentID>
<FulfillmentDate>2020-03-06T09:00:00Z</FulfillmentDate>
<FulfillmentData>
<CarrierCode>USPS</CarrierCode>
<ShippingMethod>First Class</ShippingMethod>
<ShipperTrackingNumber>9405511298370995607859</ShipperTrackingNumber>
</FulfillmentData>
<Item>
<MerchantOrderItemID>60227359610258</MerchantOrderItemID>
<MerchantFulfillmentItemID>item_5e79aa3205f4f</MerchantFulfillmentItemID>
<Quantity>1</Quantity>
<Transparencycode>AZ:SHGNCW8HKBBB8O4WZKYSHN7GYI</Transparencycode>
</Item>
</OrderFulfillment>
</Message>
</AmazonEnvelope>
Response:
<?xml version="1.0"?>
<ErrorResponse xmlns="http://mws.amazonaws.com/doc/2009-01-01/">
<Error>
<Type>Sender</Type>
<Code>InputDataError</Code>
<Message>InputDataError</Message>
<Detail />
</Error>
<RequestID>7d4ebf9a-6cb1-4b45-9191-0a00c268982a</RequestID>
</ErrorResponse>
Can anyone help me for this issue?
In my past experience, MWS is very picky about capitalization (and spelling). Check TransparencyCode. You don't have the "c" capitalized. According to the XSD, it is. Make sure everything matches.
https://images-na.ssl-images-amazon.com/images/G/01/rainier/help/xsd/release_4_1/OrderFulfillment.xsd

WPF log4net is not writing to the log file [duplicate]

This question already has an answer here:
WPF-Log4Net used inside VSIX extension did not output log when installed at target VS
(1 answer)
Closed 4 years ago.
I have a WPF solution. I downloaded log4net dll, I added log4net.config and had set the "Copy to Output Directory" value as "Copy always".
log4net.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<log4net>
<root>
<level value="ALL" />
<appender-ref ref="LogFileAppender" />
</root>
<appender name="LogFileAppender" type="log4net.Appender.FileAppender">
<file value="myapp.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="5" />
<maximumFileSize value="10MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %level %logger - %message%newline" />
</layout>
</appender>
</log4net>
</configuration>
And I added the below line in AssemblyInfo.cs:
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", Watch = true)]
then the below code in my TestWindowControl.xaml.cs
public partial class TestWindowControl : UserControl
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public TestWindowControl()
{
XmlConfigurator.Configure(new System.IO.FileInfo("log4net.config"));
log.Info("info testing");
log.Debug("debug testing");
log.Error("error testing");
log.Fatal("fatal testing");
log.Warn("warn testing");
}
}
But logs are not writing to the file. It's working in Console application but not working for WPF. Am I missing something?
Try to set the filter inside appender
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="WARN" />
<levelMax value="ERROR" />
</filter>

Amazon MWS XML how to specify shipping template

Would appreciate some sample XML. I simply want to specify which shipping template should apply to any given product.
Amazon support pointed me to https://images-na.ssl-images-amazon.com/images/G/01/rainier/help/xsd/release_4_1/Override.xsd but I fail to see how to specify the shipping template.
I was hoping there was something simple that could be included as part of _POST_PRODUCT_PRICING_DATA_ such as
<shipping_template>template1</shipping_template>
but I don't see that.
Thanks!
You should use the _POST_PRODUCT_DATA_
Here is the xml example that works for amazon.com:
<AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
<Header>
<DocumentVersion>1.01</DocumentVersion><MerchantIdentifier>XYZ</MerchantIdentifier></Header>
<MessageType>Product</MessageType>
<Message>
<MessageID>1</MessageID>
<OperationType>PartialUpdate</OperationType>
<Product><SKU>YOURSKUNAME</SKU>
<DescriptionData>
<Title>Sometitle</Title><MerchantShippingGroupName>SHIPPINGTEMPLATENAME</MerchantShippingGroupName></DescriptionData>
</Product>
</Message>
</AmazonEnvelope>
Unfortunately it looks like the <Title> is mandatory even if you just want to change shipping template. If you want to change multiple skus at once just add more <Message> to the feed.
I Use this XML Feed for shipping orders and it works:
<?xml version="1.0"?>
<AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
<Header>
<DocumentVersion>1.01</DocumentVersion>
<MerchantIdentifier>MERCHANT_IDENTIFIER</MerchantIdentifier>
</Header>
<MessageType>OrderFulfillment</MessageType>
<Message>
<MessageID>MESSAGE_ID2</MessageID>
<OrderFulfillment>
<AmazonOrderID>123-1234567-1234567</AmazonOrderID>
<FulfillmentDate>SHIP_DATE</FulfillmentDate> //$ship_date = date("c");
<FulfillmentData>
<CarrierCode>Royal Mail</CarrierCode>
</FulfillmentData>
</OrderFulfillment>
</Message>
<Message>
<MessageID>MESSAGE_ID2</MessageID>
<OrderFulfillment>
<AmazonOrderID>321-7654321-7654321</AmazonOrderID>
<FulfillmentDate>SHIP_DATE</FulfillmentDate> //$ship_date = date("c");
<FulfillmentData>
<CarrierCode>Royal Mail</CarrierCode>
</FulfillmentData>
</OrderFulfillment>
</Message>
.
.
.
</AmazonEnvelope>
$parameters = array(
'Merchant' => $MERCHANT_ID,
'MarketplaceIdList' => $marketplaceIdArray,
'FeedType' => '_POST_ORDER_FULFILLMENT_DATA_',
'FeedContent' => $feedHandle,
'PurgeAndReplace' => false, //Leave this PurgeAndReplace to false so that it want replace whole product in amazon inventory
'ContentMd5' => base64_encode(md5(stream_get_contents($feedHandle), true))
);
rewind($feedHandle);
$request = new MarketplaceWebService_Model_SubmitFeedRequest($parameters);
$return_feed = invokeSubmitFeed($service, $request);
fclose($feedHandle);

Resources