How to add top level element using Linq to XML - c#-4.0

Assuming I have a xdocument called xd, with the following xml already created.
<Alert>
<Source>
<DetectTime>12:03:2010 12:22:21</DetectTime>
</Source>
</Alert>
How would I be able to add another Alert element, such that the xml becomes:
<Alert>
<Source>
<DetectTime>12:03:2010 12:22:21</DetectTime>
</Source>
</Alert>
<Alert>
</Alert>
Adding an additional elements seems to be fairly easy, but when adding in a top level element it excepts.

Your desired XML structure is invalid; you need a root element in order to add another "Alert" node. The following code shows how to add it when a root node exists:
var xdoc = XDocument.Parse(#"<root>
<Alert>
<Source>
<DetectTime>12:03:2010 12:22:21</DetectTime>
</Source>
</Alert>
</root>");
xdoc.Root.Add(new XElement("Alert"));
Console.WriteLine(xdoc);
The above code produces <Alert /> since no child nodes are added to it (this will change once you add to it). If you want the closing tag as you have shown you can use xdoc.Root.Add(new XElement("Alert", String.Empty)); instead.
To verify that your desired output has an invalid structure you can try parsing it using XDocument.Parse similar to what I've shown above.

Related

Use custom parameters in JSON Layout [Log4j 2]

I'm confused about the meaning of property substitution, lookups and layout parameters in Log4j 2. The documentation mentions that JSON layout supports custom fields. However it doesn't seem to support conversion patterns like %d{ISO8601}, %m, %l and the like. it does however support Lookups.
Thus when I define in the xml:
<JsonLayout complete="false" compact="false">
<KeyValuePair key="#timestamp" value="%d{ISO8601}" />
<KeyValuePair key="message" value="%message" />
<KeyValuePair key="process.thread.name" value="%tn" />
</JsonLayout >
As output I simply get the strings %d{ISO8601}, %message... instead of the values.
What I'm trying to achieve is a JSON layout where I can include parameters similar to Pattern Layout where I simply write <pattern>%d %p %C{1.} [%t] %m%n</pattern> to get what I want. Or, alternatively, should I use the Pattern layout and stitch together a string in JSON Format, making use of the Pattern Layout's JSON encoding %enc{%m}{JSON}?
The GelfLayout currently supports a messagePattern attribute that will format just the message field in the JSON using the patternLayout. I have planned to add this to the JSONLayout as well but have not done it yet. There is a new JsonTemplateLayout that is in the final stages of being merged into Log4j 2 that will also support this. You could either work from the current pull request to get the Layout or wait for the Log4j 2.14.0 release when likely both options will be available.

Dicttoxml Module tags

I have been trying to make use of this module for some time now. I have many lists of dictionaries, that I want to convert into xml format. However, I want each list to essentially have its own 'table'. However When I try doing something along the lines of:
xml = dicttoxml.dictoxml(myList, root = False,
custom_root = "MyName",
attr_type = False)
I get every dict displayed as an <item> type. Shouldn't this produce what the module's owner refers to as an "xml snippet" that also is identified by the custom_root name?
Essentially I want each list to have its own identifier but not be created as 'root'. Basically where the following would have each item number associated to a certain list. Either encapsulating the whole list or each dict in the list would be suitable, I believe.
<root>
<item1>
#dict info
</item1>
<item2>
#dict info
</item2>
</root>
I fixed my problem by using just the custom_root variable in my call and leaving root = True. Then, I stripped the leading
b'<?xml version="1.0" encoding="UTF-8" ?>'
by calling
xml.partition(b'<?xml version="1.0" encoding="UTF-8" ?>')[2]
From then on, I created a file with <root> </root> tags and had the xml i created appended in between these tags.

Copy elements using xsl:copy-of without attributes

I have a xml like shown below
<?xml version="1.0" encoding="UTF-8"?>
<schools>
<city>Marshall</city>
<state>Maryland</state>
<highschool>
<schoolname>Marshalls</schoolname>
<department id="1">
<deptCode seq="1">D1</deptCode>
<deptName seq="2">Chemistry</deptName>
<deptHead seq="3">Henry Carl</deptHead>
<deptRank seq="4">L</deptRank>
</department>
<department id="2">
..
..
..
</highschool>
</schools>
In XSL i am copying the contents from department based on deptCode using
<xsl:copy-of select="*">
This produces result with all the attributes in the element tags.
Is it possible to ignore the attributes while using xsl:copy-of?
The desired result is like shown below
<deptCode>D1</deptCode>
<deptName>Chemistry</deptName>
<deptHead>Henry Carl</deptHead>
<deptRank>L</deptRank>
xsl:valueOf is working as required but i am trying to know if it
can be done with in xsl:copy-of? As a note, in my requirement, there are nearly 5 or 6 attributes for each element. Can someone please help? Thanks in Advance..
regards
Udayakiran
xsl:valueOf is working as required but i am trying to know if it can
be done with in xsl:copy-of?
No. xsl:copy-of is a package deal, you cannot pick and choose. To avoid repetitive coding, use a template matching department/*.

SlowCheetah transform ignores multiple conditions

I have a WCF configuration file that I am trying to transform with SlowCheetah. For development use, we want to include the MEX endpoints, but when we release the product, these endpoints should be removed on all services except one. The server for which it should be left has the following endpoint:
<endpoint address="MEX"
binding="mexHttpBinding"
contract="IMetadataExchange" />
The ones that should be removed are as follows:
<endpoint address="net.tcp://computername:8001/WCFAttachmentService/MEX"
binding="netTcpBinding"
bindingConfiguration="UnsecureNetTcpBinding"
name="WCFAttachmentServiceMexEndpoint"
contract="IMetadataExchange" />
The transform I am using is:
<service>
<endpoint xdt:Locator="Condition(contains(#address, 'MEX') and not(contains(#binding, 'mexHttpBinding')))" xdt:Transform="RemoveAll" />
</service>
However, when I run this, ALL MEX endpoints are removed from the config file including the one that I wish to keep. How do I make this work properly?
The Locator Condition expression that selects the nodes seems to be correct. If you had only the two endpoints you posted in your example, this expression will select the second endpoint.
According to the documentation the Transform attribute RemoveAll should "remove the selected element or elements." Based on the information you posted it's not working as expected, since the first element was not selected and was removed anyway. Based on this StackOverflow answer it seems to me that the issue is with Condition. I'm not sure if that's a bug (it's poorly documented), but you could try some alternative solutions:
1) Using XPath instead of Condition. The effective XPath expression that is applied to your configuration file as a result of the Condition expression is:
/services/service/endpoint[contains(#address, 'MEX') and not(contains(#binding, 'mexHttpBinding'))]
You should also obtain the same result using the XPath attribute instead of Condition:
<endpoint xdt:Locator="XPath(/services/service/endpoint[contains(#address, 'MEX')
and not(contains(#binding, 'mexHttpBinding'))])" xdt:Transform="RemoveAll" />
2) Using Match and testing an attribute such as binding. This is a simpler test, and would be IMO the preferred way to perform the match. You could select the nodes you want to remove by the binding attribute
<endpoint binding="netTcpBinding" xdt:Locator="Match(binding)" xdt:Transform="RemoveAll" />
3) UsingXPath instead of Match in case you have many different bindings and only want to eliminate only those which are not mexHttpBinding:
<endpoint xdt:Locator="XPath(/services/service/endpoint[not(#binding='mexHttpBinding'))" xdt:Transform="RemoveAll" />
4) Finally, you could try using several separate statements with Condition() or Match() to individually select the <endpoint> elements you wish to remove, and use xdt:Transform="Remove" instead of RemoveAll.

Parsing Tableau xml does not preserve original file

I try to work programmatically on Tableau desktop file (which are just xml file in spite of their .twb extension). I have many problem with lxml which doesn't preserve original content. To facilitate the explanation, imagine you have a test.xml file which contain the following text:
<column caption='Choix Découpage' name='[Aujourd&apos;Hui Parameter (copy 2)]'>
<member name='Nb d&apos;annulations' default-format='n#,##0.00" annulations";-#,##0.00" annulations"' />
<run>
:</run>
<calculation formula='iif([FAC_TYPE] = &apos;Avoir&apos; , [Calculation_1378101492427309057], null)' />
<alias key='"Billetterie Ferroviaire"' value='Train ticketing' />
</column>
Now let's parse it:
tree = etree.parse('test.xml')
root = tree.getroot()
print(etree.tostring(root,pretty_print=True,).decode("utf-8"))
When you run the code we can notice:
' becomes "
é becomes é Edit: resolved for this part
&apos; becomes '
How could i preserve the original ? (It would help me a lot when i try to check the diff with git in spite of showing all the useless change that are operated automatically)
Edit: I notice an other problem, when i run the folowing code:
[node.attrib['key'] for node in root.xpath("//alias")]
I got the result: ['"Billetterie Ferroviaire"'] and I am now unable to query with xpath if i am looking for the node whose attribute "key" is the original "Billetterie Ferroviaire" (root.xpath('//[#key="Billetterie Ferroviaire"]) doesn't work)

Resources