Best xml questions in April 2012

How to add header and Footer to each activity in android

6 votes

I want to add a ImageButton, a button and a textview in each of my activity at top and bottom.I thought of using header and footer. So I want to add a header and footer in each of my Android Activity. I don't have any idea of how to do that. I don't need source code of how to write a header or footer. What i want to know is where i have to define that header and footer means do i need to add a header and footer in each xml file or do i need to define two header.xml or footer.xml and use these xml files in each of other xml files. Or is there any other way mean like using a reference from the java file of that activity. Any help Appreciated.

Define two separate files header.xml and footer.xml and and than use

`<include layout="@layout/header"/>

<include layout="@layout/footer"/>`

IXMLDocument.SaveToFile() uses tab character for indentation instead of spaces

5 votes

I have an XML file, which is originally formatted using space indents (2 spaces for each nested item).

When I load and save this file using IXMLDocument, space indents are changing to the tab characters (code #9).

Here is the code:

 var
   FileName: String;
   Document: IXMLDocument;
 ...
 Document := XMLDoc.LoadXMLDocument(FileName);
 Document.SaveToFile(FileName);

I tried to use NodeIndentStr property - no result:

 Document := XMLDoc.LoadXMLDocument(FileName);
 Document.NodeIndentStr := '  ';
 Document.SaveToFile(FileName);

Used FormatXMLData too - no result:

 Document := XMLDoc.LoadXMLDocument(FileName);
 Document.XML.Text := XMLDoc.FormatXMLData(Document.XML.Text);
 Document.Active := True;
 Document.SaveToFile(FileName);

How can I save with space indents instead of tab characters?

There is an option in IXMLDocument where the parser can be told to preserve white spaces.

Use it like this :

Document.ParseOptions := 
  Document.ParseOptions+[poValidateOnParse]+[poPreserveWhiteSpace]; 

Disclaimer: I haven't tried it.

Looking for suggestions to reorder images in a user interface

5 votes

I'm building an app that lets a user select a folder, the app then collects all of the images in that folder and allows the user to add captions and descriptions. Essentially an interface that generates XML that drives a Flash photo gallery.

I'm now looking at various ways to provide some way for the user to rearrange those images in a different order. I'm leaning toward some window of thumbs that allow drag and drop functionality, but...
1 - I'm a total Java noob so this is beyond my skill set today. Though I am willing to tackle it if... 2 - Is this a good GUI user friendly approach? Are there other methods that will provide a better user experience?

Essentially, I'm looking for ideas, a kind of what have you seen and used that is elegant and easy to use that provides this functionality.

If there are code examples that can be added, that would be perfect...

Consider

  • using a JList which is built to easily display images and would allow drag & drop functionality with a minimum of coding.
  • For creating the XML, perhaps try JAXB, although I would consider this to be a bit more than "basic" Java.

How can I query a value in SQL Server XML column

5 votes

I have following XML stored in a XML column (called Roles) in a SQL Server database.

<root>
   <role>Alpha</role>
   <role>Beta</role>
   <role>Gamma</role>
</root>

I'd like to list all rows that have a specific role in them. This role passed by parameter.

select
  Roles
from
  MyTable
where
  Roles.value('(/root/role)[1]', 'varchar(max)') like 'StringToSearchFor'

These pages will show you more about how to query XML in T-SQL:

Querying XML fields using t-sql

Flattening XML Data in SQL Server

EDIT

After playing with it a little bit more, I ended up with this amazing query that uses CROSS APPLY. This one will search every row (role) for the value you put in your like expression...

Given this table structure:

create table MyTable (Roles XML)

insert into MyTable values
('<root>
   <role>Alpha</role>
   <role>Gamma</role>
   <role>Beta</role>
</root>')

We can query it like this:

select * from 

(select 
       pref.value('(text())[1]', 'varchar(32)') as RoleName
from 
       MyTable CROSS APPLY

       Roles.nodes('/root/role') AS Roles(pref)
)  as Result

where RoleName like '%ga%'

You can check the SQL Fiddle here: http://sqlfiddle.com/#!3/ae0d5/13

XSLT: How to output localized data only?

4 votes

Given the following XML:

<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="form.xsl"?>
<Document>
  <Translations>
    <Translation name="Resource">Invariant Resource</Translation>
    <Translation name="Resource" lang="en">English Resource</Translation>
    <Translation name="Resource" lang="en-CA">Canadian English Resource</Translation>
    <Translation name="Resource" lang="en-GB">British English Resource</Translation>
    <Translation name="Message">Invariant Message</Translation>
    <Translation name="Message" lang="en">English Message</Translation>
    <Translation name="Message" lang="en-CA">Canadian English Message</Translation>
    <Translation name="Message" lang="en-AU">Australian English Message</Translation>
  </Translations>
</Document>

I need to select a set of Translation elements such that the set contains unique values for the "name" attribute, and the "best match" for a given locale ('en-US', 'es-MX', 'fr', etc.). When I say best match, I would like to first look for an element with the full matching locale, then look for a match based on just the first two characters, then look for an element with no lang specified.

For example, if I pass in a locale of 'en-CA' when transforming the above data, I would like to get the following two elements:

<Translation name="Resource" lang="en-CA">Canadian English Resource</Translation>
<Translation name="Message" lang="en-CA">Canadian English Message</Translation>

But if I pass in 'en-GB', I would like to get:

<Translation name="Resource" lang="en-GB">British English Resource</Translation>
<Translation name="Message" lang="en">English Message</Translation>

And finally if I pass in a value such as 'es' or 'es-MX', I would expect to get:

<Translation name="Resource">Invariant Resource</Translation>
<Translation name="Message">Invariant Message</Translation>

I'm extremely new to XSLT, but I think I have something that works. I just need to know if there is a better way to do it (simpler, more elegant, more performant, etc.)

Here's my first stab at it:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output encoding="utf-8" indent="yes" method="xml" omit-xml-declaration="yes"/>

  <xsl:key match="Translation" name="TranslationName" use="concat(@name,':',@lang)"/>

  <xsl:template match="/">
    <!-- locale parameter for translation -->
    <xsl:param name="locale"/>

    <xsl:for-each select="Document/Translations/Translation[@lang=$locale or @lang=substring($locale,1,2) or not(@lang)]">
      <xsl:choose>
        <xsl:when test="@lang=$locale and count(key('TranslationName', concat(@name,':',$locale)))=1">
          <xsl:element name="p">
            <xsl:value-of select="."/>
          </xsl:element>
        </xsl:when>
        <xsl:when test="@lang=substring($locale,1,2) and count(key('TranslationName', concat(@name,':',$locale)))=0">
          <xsl:element name="p">
            <xsl:value-of select="."/>
          </xsl:element>
        </xsl:when>
        <xsl:when test="not(@lang) and count(key('TranslationName', concat(@name,':',$locale))|key('TranslationName', concat(@name,':',substring($locale,1,2))))=0">
          <xsl:element name="p">
            <xsl:value-of select="."/>
          </xsl:element>
        </xsl:when>
      </xsl:choose>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

This is my first time to post a question, so please let me know if I need to add/edit/remove anything.

Thanks!

if you could use msxsl:node-set or the like, you might do it like:

<xsl:template match="/">
    <xsl:param name="locale" select="'en-AU'"/>
<!-- locale parameter for translation -->
    <xsl:variable name="sorted">
        <xsl:for-each select="Document/Translations/Translation">
            <xsl:sort select="@name"/>
            <xsl:sort select="not(@lang=$locale)"/>
            <xsl:sort select="not(starts-with(@lang, substring($locale,1,2)))"/>
            <xsl:sort select="@lang"/>
            <xsl:copy-of select="."/>
        </xsl:for-each>
    </xsl:variable>
    <xsl:for-each select="msxsl:node-set($sorted)/*">
        <xsl:if test="position() = 1 or @name!=preceding-sibling::*[1]/@name">
            <xsl:copy-of select="."/>
        </xsl:if>
    </xsl:for-each>
</xsl:template>

P.S. This one might work on standard 1.0

<xsl:template match="/">
    <xsl:param name="locale" select="'en-AU'"/>
<!-- locale parameter for translation -->
    <xsl:variable name="path" select="Document/Translations/Translation"/>
    <xsl:for-each select="$path">
        <xsl:variable name="curName" select="$path[@name=current()/@name]"/>
        <xsl:if test="count($curName[1] | .)=1">
            <xsl:for-each select="$curName">
            <xsl:sort select="not(@lang=$locale)"/>
            <xsl:sort select="not(starts-with(@lang, substring($locale,1,2)))"/>
            <xsl:sort select="@lang"/>
            <xsl:if test="position()=1">
                <xsl:copy-of select="."/>
            </xsl:if>
            </xsl:for-each>
        </xsl:if>
    </xsl:for-each>
</xsl:template>

P.P.S. If you don't want to sort you might just do the filtering (preserves document order). Also, different grouping mechanism:

<xsl:template match="/">
    <xsl:param name="locale" select="'en'"/>
    <xsl:variable name="locale-lang" select="substring($locale,1,2)"/>
<!-- locale parameter for translation -->
    <xsl:variable name="path" select="Document/Translations/Translation"/>
    <xsl:for-each select="$path[not(preceding-sibling::Translation/@name=@name)]">
        <xsl:variable name="curName" select="$path[@name=current()/@name]"/>
        <xsl:variable name="test1" select="$curName[@lang=$locale]"/>
        <xsl:variable name="test2" select="$curName[@lang=$locale-lang]"/>
        <xsl:variable name="test3" select="$curName[starts-with(@lang, $locale-lang)]"/>
        <xsl:variable name="test4" select="$curName[not(@lang)]"/>
        <xsl:choose>
            <xsl:when test="$test1">
                <xsl:copy-of select="$test1[1]"/>
            </xsl:when>
            <xsl:when test="$test2">
                <xsl:copy-of select="$test2[1]"/>
            </xsl:when>
            <xsl:when test="$test3">
                <xsl:copy-of select="$test3[1]"/>
            </xsl:when>
            <xsl:when test="$test4">
                <xsl:copy-of select="$test4[1]"/>
            </xsl:when>
        </xsl:choose>
    </xsl:for-each>
</xsl:template>

Select Xml Node using Linq to XML

4 votes

my Xml file :

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfCustomer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Customer>
        <CustomerId>1f323c97-2015-4a3d-9956-a93115c272ea</CustomerId>
        <FirstName>Aria</FirstName>
        <LastName>Stark</LastName>
        <DOB>1999-01-01T00:00:00</DOB>
    </Customer>
    <Customer>
        <CustomerId>c9c326c2-1e27-440b-9b25-c79b1d9c80ed</CustomerId>
        <FirstName>John</FirstName>
        <LastName>Snow</LastName>
        <DOB>1983-01-01T00:00:00</DOB>
    </Customer>
</ArrayOfCustomer>  

my attempt :

XElement toEdit = 
    (XElement)doc.Descendants("ArrayOfCustomer")
                 .Descendants("Customer")
                 .Where(x => Guid.Parse((x.Descendants("CustomerId") as XElement).Value) == customer.CustomerId)
                 .First<XElement>();

this throws the following exception :

 Object reference not set to an instance of an object.

1) isn't x an XElement?

2) is this a proper where lambda for selecting an Xml node?

3) and of course how would you find this node according to CustomerId?

Your problem is that Descendents and Where return an IEnumerable<XElement> not a single XElement which is what you're after. You can fix this like this:

XElement toEdit = doc.Descendants("ArrayOfCustomer")
                     .Descendants("Customer")
                     .Where(x => Guid.Parse(x.Descendants("CustomerId").Single().Value) == customer.CustomerId)
                     .FirstOrDefault();

How to add third party dll in Tridion for C# TBB?

4 votes

I am creating a C# TBB. I have the XML code as shown below.

<content>
  <ah>123</ah>
  <ph>456</ph>
  <body>
    <sc>hi</sc>
    <value>aa</value>
    <value>bb</value>
    <value>cc</value>
    <value>dd</value>
    <value>dd</value>
  </body>
  <body>
    <sc>hello</sc>
    <value>ee</value>
    <value>ddff</value>
  </body>
</content>

C# TBB code:

using (MemoryStream ms = new MemoryStream())
{
XmlTextWriter securboxXmlWriter = new XmlTextWriter(ms, new System.Text.UTF8Encoding(false));
securboxXmlWriter.Indentation = 4;
securboxXmlWriter.Formatting = Formatting.Indented;
securboxXmlWriter.WriteStartDocument();


securboxXmlWriter.WriteStartElement("component");

securboxXmlWriter.WriteAttributeString("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
securboxXmlWriter.WriteAttributeString("xmlns", "http://www.w3.org/1999/xhtml");


securboxXmlWriter.WriteStartElement("content");
securboxXmlWriter.WriteStartElement("wire:wire");
securboxXmlWriter.WriteStartElement("wire:si");

securboxXmlWriter.WriteStartElement("wg:ah");
securboxXmlWriter.WriteElementString("text", package.GetValue("Component.ah"));
securboxXmlWriter.WriteEndElement();

securboxXmlWriter.WriteStartElement("wg:ph");
securboxXmlWriter.WriteElementString("nlt", package.GetValue("Component.ph"));
securboxXmlWriter.WriteEndElement();

securboxXmlWriter.WriteEndElement();
securboxXmlWriter.WriteEndElement();
securboxXmlWriter.WriteEndElement();
securboxXmlWriter.WriteEndElement();
securboxXmlWriter.WriteEndDocument();
securboxXmlWriter.Flush();
securboxXmlWriter.Close();



Item output = package.GetByName("Output");

if (output != null)
{
package.Remove(output);
}

package.PushItem("Output", package.CreateStringItem(ContentType.Xml, Encoding.UTF8.GetString(ms.ToArray())));
}

In the XML code "body" tag is occured multiple times. I need to extract the each and every "body" tag content. For that purpose I am using HTML agility pack. To make it work in the C# TBB, How to add the HTML agility pack DLL to the Tridion system? And also please provide a sample code snippet in html agility to loop through the body tags.

If HTML Agility is not going to work with C# TBB then please suggest me a way how to get the "body" tag content?

Thanks in advance.Early response is appreciated.

You will need to place the third party DLL in the Global Assembly Cache (GAC). For the sample agility pack code I suggest you ask a separate question for that with a more specific title.

Can't changing an xml value with PHP

4 votes

This is my XML-file:

<todos>
  <todo>
    <titel>sasd</titel>
    <erstellt>2012-04-30 17:19:21</erstellt>
    <erledigen_bis>2012-05-03</erledigen_bis>
    <erledigt>Nein</erledigt>
    <thingstodo>sffsdfdf</thingstodo>
  </todo>
</todos>

Now I want to change the value inside of the <erledigt> tag to 'Ja'.

I tried this with the following code:

<?php
$filename = 'xml/todos.xml';

$xmlDoc = new DOMDocument();
$xmlDoc->load('xml/todos.xml');

$todos = $xmlDoc->getElementsByTagName('todo');         

foreach ($todos as $todo) {

    $titel = $todo->getElementsByTagName('titel');
    $actualTitel = $titel->item(0)->nodeValue;
    $paramTitel = $_GET["titel"];

    $erstellt = $todo->getElementsByTagName('erstellt');    
    $actualTimestamp = $erstellt->item(0)->nodeValue;
    $paramTimestamp = $_GET["timestamp"];

    if ($paramTitel == $actualTitel && $paramTimestamp == $actualTimestamp) {

        $todo->erledigt= 'Ja';

    }
}

$xmlDoc->save($filename);

header('Location: todo.php');
?>

Please help me, I searched for about 5 hours on the web and couldn't find any solution for my problem.

$todo->erledigt doesn't work in DOMDocument, only SimpleXML can do that. You need to use getElementsByTagName again.

You also need to use nodeValue to get/set the value of the element.

if ($paramTitel == $actualTitel && $paramTimestamp == $actualTimestamp) {
    $todo->getElementsByTagName('erledigt')->item(0)->nodeValue = 'Ja';
}

DEMO: http://codepad.org/xJbQmO2u

How does git rerere figure out the similarities between two conflicts?

3 votes

Ok, here goes my Q. Does "git rerere" compare hashes of two files to figure out the resolution? That is, say I have an XML file which contains this tag:

<number>12</number>

When I have a conflict, that number usually gets changed to something like 13, 14, etc, so I'm stuck with:

<<<<<<<
<number>12</number>
=======
<number>13</number>
>>>>>>>

Can rerere automatically resolve this conflict even if the numbers are not the same as the last time? I always want it to resolve it in such a way that it takes the higher number (in example above, 13). So if it records the resolution for numbers 12 and 13, will it resolve conflict with different numbers? I have a suspicion it won't, but might as well ask.

Can rerere automatically resolve this conflict even if the numbers are not the same as the last time? I always want it to resolve it in such a way that it takes the higher number (in example above, 13).

No, git rerere can't do that.

When you use git rerere, you're asking it to remember two things:

  • a specific conflict (that is, the literal text of the hunks which are in conflict and the files that they're in); and
  • the resolution that should be applied to that conflict

Because these are hunk literals (e.g. "replace 12 with 13") rather than some kind of transformation function (e.g. "replace N with N + 1"), git is unable to infer that you want to replace each number with a higher one.

If git later encounters a conflict that doesn't match any of its previously remembered conflicts, it's just as if it's never encountered that conflict at all, even if it might be similar (as in your example where the numbers are always one off).

One way out is possible if this XML file is automatically generated. In that case, you might want to consider not keeping it in source control at all, and instead, generating it at runtime. Then you'll never have the corresponding conflicts to clean up.

Another approach is to avoid git rerere altogether, and instead write a custom merge driver that will resolve things for you. I've done this before, but it's a non-trivial amount of work, and it requires you to write and test some code. If you're interested in this strategy, see the link in Karl's answer.

Convert XML in a column to a table

3 votes

My input is an XML column in SQL Server table like this:

<word Entry="Ketab" Affix="miyanvand" Pos="esm" Derv="Jamed" />

Desired output: a table like this:

Entry    | Affix       | Pos     | Derv
Ketab    | miyanvand   | esm     | Jamed

If you are using sql-server 2005+. Then maybe something like this:

DECLARE @xml XML=
'<word Entry="Ketab" Affix="miyanvand" Pos="esm" Derv="Jamed" />'

SELECT 
   Y.ID.value('(@Entry)[1]', 'Varchar(100)') as [Entry],
   Y.ID.value('(@Affix)[1]', 'Varchar(100)') as [Affix],
   Y.ID.value('(@Pos)[1]', 'Varchar(100)') as [Pos],
   Y.ID.value('(@Derv)[1]', 'Varchar(100)') as [Derv]
FROM @xml.nodes('/word') as Y(ID)

Or you can also do it like this:

DECLARE @xml XML=
'<word Entry="Ketab" Affix="miyanvand" Pos="esm" Derv="Jamed" />'

SELECT 
    @xml.value('(/word/@Entry)[1]', 'varchar(50)') as Entry,
    @xml.value('(/word/@Affix)[1]', 'varchar(50)') as Affix,
    @xml.value('(/word/@Pos)[1]', 'varchar(50)') as Pos,
    @xml.value('(/word/@Derv)[1]', 'varchar(50)') as Derv

Or if you have a table. Then something like this:

DECLARE @tbl TABLE(ID INT,someXML XML)
INSERT INTO @tbl
VALUES
    (1,'<word Entry="Ketab" Affix="miyanvand" Pos="esm" Derv="Jamed" />')

SELECT
    tbl.ID,
    Y.ID.value('(@Entry)[1]', 'Varchar(100)') as [Entry],
    Y.ID.value('(@Affix)[1]', 'Varchar(100)') as [Affix],
    Y.ID.value('(@Pos)[1]', 'Varchar(100)') as [Pos],
    Y.ID.value('(@Derv)[1]', 'Varchar(100)') as [Derv]
FROM
    @tbl AS tbl
    CROSS APPLY someXML.nodes('/word') as Y(ID)

Remove duplicate node based on more than one attribute in xml using xslt

3 votes

I have this input XML which needs to be transformed with an xslt

<root>
    <node id="a">
        <section id="a_1" method="run">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
        <section id="a_2">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
        <section id="a_1" method="run">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
    </node>
    <node id="b">
        <section id="b_1" method="create">
            <user id="b_1a">
                <attribute>
                    <name>John</name>
                </attribute>
            </user>
            <user id="b_1b">
                <attribute>a</attribute>
            </user>
        </section>
        <section id="b_1" method="create">
            <user id="b_1c">
                <attribute>a</attribute>
            </user>
        </section>
        <section id="b_2">
            <user id="b_1a">
                <attribute>
                    <name>John</name>
                </attribute>
            </user>
        </section>
    </node>
</root>

Expected output:

<root>
    <node id="a">
        <section id="a_1">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
        <section id="a_2">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
    </node>
    <node id="b">
        <section id="b_1" method="create">
            <user id="b_1a">
                <attribute>
                    <name>John</name>
                </attribute>
            </user>
            <user id="b_1b">
                <attribute>a</attribute>
            </user>
        </section>

        <section id="b_2">
            <user id="b_1a">
                <attribute>
                    <name>John</name>
                </attribute>
            </user>
        </section>
    </node>
</root>

It does not matter which node will be eliminated, as long as it has the same element name, id and method, one of them will be removed. Any idea what the xsl looks like ?

Note: the element name can be anything doesn't have to be and it has more than one element name in the whole file; as long as it has the same element name, id and attribute (ex. method=create) one of them will be eliminated.

Thanks very much. cheers, John

I. Here is a short and efficient (using keys) XSLT 1.0 transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kElemWithAttribs" match="*[@id and @method]"
  use="concat(name(), '+', @id, '+', @method)"/>

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match=
  "*[@id and @method
    and
     not(generate-id()
        =
         generate-id(key('kElemWithAttribs',
                         concat(name(), '+', @id, '+', @method)
                         )[1]
                    )
         )
     ]"/>
</xsl:stylesheet>

When this transformation is applied on the provided XML document:

<root>
    <node id="a">
        <section id="a_1" method="run">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
        <section id="a_2">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
        <section id="a_1" method="run">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
    </node>
    <node id="b">
        <section id="b_1" method="create">
            <user id="b_1a">
                <attribute>
                    <name>John</name>
                </attribute>
            </user>
            <user id="b_1b">
                <attribute>a</attribute>
            </user>
        </section>
        <section id="b_1" method="create">
            <user id="b_1c">
                <attribute>a</attribute>
            </user>
        </section>
        <section id="b_2">
            <user id="b_1a">
                <attribute>
                    <name>John</name>
                </attribute>
            </user>
        </section>
    </node>
</root>

the wanted, correct result is produced:

<root>
   <node id="a">
      <section id="a_1" method="run">
         <item id="0">
            <attribute>
               <color>Red</color>
            </attribute>
         </item>
      </section>
      <section id="a_2">
         <item id="0">
            <attribute>
               <color>Red</color>
            </attribute>
         </item>
      </section>
   </node>
   <node id="b">
      <section id="b_1" method="create">
         <user id="b_1a">
            <attribute>
               <name>John</name>
            </attribute>
         </user>
         <user id="b_1b">
            <attribute>a</attribute>
         </user>
      </section>
      <section id="b_2">
         <user id="b_1a">
            <attribute>
               <name>John</name>
            </attribute>
         </user>
      </section>
   </node>
</root>

Explanation:

Using the Muenchian method for grouping with a composite key. Here we ignore (delete) every node that isn't the first in a group.


II. XSLT 2.0 solution -- even shorter and not less efficient:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="node">
  <xsl:copy>
    <xsl:apply-templates select="@*"/>

    <xsl:for-each-group select="*"
         group-by="concat(name(), '+', @id, '+', @method)">
      <xsl:apply-templates select="."/>
    </xsl:for-each-group>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

Explanation:

Proper use of xsl:for-each-group with a group-by attribute.

How do i make Xpath search case insensitive

3 votes

I'm currently making a xpath search, I've got the the search working but I need to make it case insensitive. The xml file I'm using is 1.0 which from my research means I've got to use some thing called a translate function but I'm unsure of how to do this.

Here is my search file :

$holidayDoc = simplexml_load_file('holidays.xml');      

// fetch data from form
$txtSearch = $_GET['txtSearch'];

$qry = "//channel/item[contains(.,\"$txtSearch\")]";


$holidays = $holidayDoc->xpath($qry);   // do the xpath query 
// now loop through all the students

echo "Showing title search results for $txtSearch";

foreach ($holidays as $holiday) 
{

 echo "<p><a href=\"{$holiday->link}\">{$holiday->title}</a></p>
    <p><small>$holiday->pubDate</small></p>";

Any help would be greatly appreciated thanks.

XPath 1.0 :

$qry = "//channel/item[contains(
 translate(., 'ABCDEFGHJIKLMNOPQRSTUVWXYZ', 'abcdefghjiklmnopqrstuvwxyz'),  
 translate($search, 'ABCDEFGHJIKLMNOPQRSTUVWXYZ', 'abcdefghjiklmnopqrstuvwxyz'))]"

XPath 2.0 :

$qry = "//channel/item[lower-case(.) = lower-case($search)]"

Both replace all upper case to lower case.

Deserializing xml to class, trouble with list<>

3 votes

I have the following XML

<map version="1.0">
    <properties>
        <property name="color" value="blue" />
        <property name="size" value="huge" />
        <property name="texture" value="rugged" />
    </properties>
</map>

I am trying to write classes that I can deserialize this into, this is what I have:

[XmlRoot("map")]
public class MyMap
{
    [XmlAttribute("version")]
    public decimal Version { get; set; }
    [XmlElement("properties")]
    public List<MyProperty> Properties { get; set; }
}

public class MyProperty
{
    [XmlAttribute("name")]
    public string Name { get; set; }
    [XmlAttribute("value")]
    public string Value { get; set; }
}

The problem is that I cant seem to read the property list, I just get one entry and it has null in both Name and Value.

Are there some magic attributes I need to set to get this to work?

You should change MyMap as below. XmlArray and XmlArrayItem are the magic attributes

[XmlRoot("map")]
public class MyMap
{
    [XmlAttribute("version")]
    public decimal Version { get; set; }
    [XmlArray("properties")]
    [XmlArrayItem("property")]
    public List<MyProperty> Properties { get; set; }
}

Performance bulk-loading data from an XML file to MySQL

3 votes

Should an import of 80GB's of XML data into MySQL take more than 5 days to complete?

I'm currently importing an XML file that is roughly 80GB in size, the code I'm using is in this gist and while everything is working properly it's been running for almost 5 straight days and its not even close to being done ...

The average table size is roughly:

Data size: 4.5GB
Index size: 3.2GB
Avg. Row Length: 245
Number Rows: 20,000,000

Let me know if more info is needed!

Server Specs:

Note this is a linode VPS

Intel Xeon Processor L5520 - Quad Core - 2.27GHZ 4GB Total Ram

XML Sample

https://gist.github.com/2510267

Thanks!


After researching more regarding this matter this seems to be average, I found this answer which describes ways to improve the import rate.

One thing which will help a great deal is to commit less frequently, rather than once-per-row. I would suggest starting with one commit per several hundred rows, and tuning from there.

Also, the thing you're doing right now where you do an existence check -- dump that; it's greatly increasing the number of queries you need to run. Instead, use ON DUPLICATE KEY UPDATE (a MySQL extension, not standards-compliant) to make a duplicate INSERT automatically do the right thing.

Finally, consider building your tool to convert from XML into a textual form suitable for use with the mysqlimport tool, and using that bulk loader instead. This will cleanly separate the time needed for XML parsing from the time needed for database ingestion, and also speed the database import itself by using tools designed for the purpose (rather than INSERT or UPDATE commands, mysqlimport uses a specialized LOAD DATA INFILE extension).