I am working on a data view web part in SP 2010 to rullup some information from sub sites. These sites contain project task lists that have tasks assigned to users. The customer wants to see a rollup that will list projects by user based on whether or not they have tasks assigned. Since a project can have multiple tasks, a project could show up under multiple users in the rollup, depending on which tasks are assigned to which users.
Long story short, I need a way to select the count of distinct projects for each user.
The xml structure is like this:
<Rows>
<Row Project="Project 1" TaskID="1" AssignedTo="Worker A" />
<Row Project="Project 1" TaskID="2" AssignedTo="Worker B" />
</Rows>
From this, I would expect the following for project counts:
Worker A: 1
Worker B: 1
I am trying the following formula:
count($nodeset[not(#Project=preceding-sibling::Row/#Project) and #AssignedTo = current()/#AssignedTo])
This returns a count of 1 project for Worker A, but it returns 0 for Worker B, because Project 1 has already been counted for Worker A.
I don't know much about xslt, but from what I understand, using current() is not the most resource-friendly method. I've tried to do the muenchian grouping but I haven't been able to get it to work. Willing to try again though. Any advice would be helpful, as, again, I stink with xslt.
This 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="kWorker" match="#AssignedTo" use="."/>
<xsl:key name="kProjByWorker" match="#Project" use="../#AssignedTo"/>
<xsl:key name="kProjWorker" match="#Project" use="concat(.,'+',../#AssignedTo)"/>
<xsl:template match="/*">
<xsl:apply-templates select=
"//#AssignedTo
[generate-id()
=
generate-id(key('kWorker',.)[1])
]"/>
</xsl:template>
<xsl:template match="#AssignedTo">
<xsl:value-of select="concat('
',.,': ')"/>
<xsl:value-of select=
"count(
key('kProjByWorker', .)
[generate-id()
=
generate-id(key('kProjWorker', concat(.,'+',current()))[1])]
)"/>
</xsl:template>
</xsl:stylesheet>
when applied on the following XML document (a more challenging version of the provided one):
<Rows>
<Row Project="Project 1" TaskID="1" AssignedTo="Worker A" />
<Row Project="Project 1" TaskID="2" AssignedTo="Worker B" />
<Row Project="Project 1" TaskID="2" AssignedTo="Worker A" />
<Row Project="Project 2" TaskID="1" AssignedTo="Worker A" />
</Rows>
produces the wanted, correct result:
Worker A: 2
Worker B: 1
Explanation:
Use of the Muenchian grouping method twice: to find all different workers, and then to find the distinct projects assigned to this worker.
Related
using xslt 3, i need to take all content elements' values, and move them to the title elements (if the title elements already exist in a record, they need to be appended with a separator like -) i now have inputted my real data, since the below solution does not solve the problem when implemented to something like:
example input:
<data>
<RECORD ID="31365">
<no>25099</no>
<seq>0</seq>
<date>2/4/2012</date>
<ver>2/4/2012</ver>
<access>021999</access>
<col>GS</col>
<call>889</call>
<pr>0</pr>
<days>0</days>
<stat>0</stat>
<ch>0</ch>
<title>1 title</title>
<content>1 content</content>
<sj>1956</sj>
</RECORD>
<RECORD ID="31366">
<no>25100</no>
<seq>0</seq>
<date>2/4/2012</date>
<ver>2/4/2012</ver>
<access>022004</access>
<col>GS</col>
<call>8764</call>
<pr>0</pr>
<days>0</days>
<stat>0</stat>
<ch>0</ch>
<sj>1956</sj>
<content>1 title</content>
</RECORD>
</data>
expected output:
<data>
<RECORD ID="31365">
<no>25099</no>
<seq>0</seq>
<date>2/4/2012</date>
<ver>2/4/2012</ver>
<access>021999</access>
<col>GS</col>
<call>889</call>
<pr>0</pr>
<days>0</days>
<stat>0</stat>
<ch>0</ch>
<title>1 title - 1 content</title>
<sj>1956</sj>
</RECORD>
<RECORD ID="31366">
<no>25100</no>
<seq>0</seq>
<date>2/4/2012</date>
<ver>2/4/2012</ver>
<access>022004</access>
<col>ΓΣ</col>
<call>8764</call>
<pr>0</pr>
<days>0</days>
<stat>0</stat>
<ch>0</ch>
<sj>1956</sj>
<title>1 title</title>
</RECORD>
<data>
with my attempt, i did not manage to move the elements, i just got an empty line where the content element existed, so please add the removal of blank lines in the suggested solution.
i believe the removal of blank lines could be fixed with the use of
<xsl:template match="text()"/>
One way to achieve this is the following template. It uses XSLT-3.0 content value templates.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" expand-text="true">
<xsl:output method="xml" indent="yes" />
<xsl:mode on-no-match="shallow-copy" />
<xsl:strip-space elements="*" /> <!-- Remove space between elements -->
<xsl:template match="RECORD">
<xsl:copy>
<xsl:copy-of select="#*" />
<title>{title[1]}{if (title[1]) then ' - ' else ''}<xsl:value-of select="content" separator=" " /></title>
<xsl:apply-templates select="node() except (title,content)" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
It's output is as desired.
If you want to separate the <content> elements with a -, too, you can simplify the core <title> expression to
<xsl:value-of select="title|content" separator=" - " />
EDIT:
All I changed was replacing chapter with RECORD, and it's working fine with Saxon-HE 9.9.1.4J. The only difference in the output is that the title element is always at the first position, but that shouldn't matter. I also added a directive to remove space between elements.
I need to populate one segment based on input values.the requirement like below
in the input payload we are getting below segment like
<charac>
<charactername>
<charactervalue>
</charac>
so the above segment may come multiple times but based on the few values only need to populate the segment for example,
<charac>
<charactername>print</charactername>
<charactervalue>123</charactervalue>
</charac>
<charac>
<charactername>comp</charactername>
<charactervalue>1234</charactervalue>
</charac>
<charac>
<charactername>pal</charactername>
<charactervalue>1235</charactervalue>
</charac>
so here only I need populate segment when charactername is equal to only print or comp
the receiver structure the segment is
<e1edl1>
<at>
<rt>
</e1edl1>
so the output should be like
<e1edl1>
<at>print</at>
<rt>123</rt>
</e1edl1>
<e1edl1>
<at>comp</at>
<rt>1234</rt>
</e1edl1>
I tried with below code
<ns0:if test="count(./charac)!=0">
<ns0:for-each select="./charac">
<e1edl1 SEGMENT="1">
<at>
<ns0:value-of select="charactername" />
</at>
<rt>
<ns0:value-of select="charactervalue" />
</rt>
</e1edl1>
</ns0:for-each>
</ns0:if>
could you please help on this.
Regards,
Janardhan
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="//charac[charactername='print' or charactername='comp']" />
</xsl:template>
<xsl:template match="charac">
<e1edl1>
<at><xsl:value-of select="charactername" /></at>
<rt><xsl:value-of select="charactervalue" /></rt>
</e1edl1>
</xsl:template>
</xsl:stylesheet>
I am building a Sharepoint search results page to find the answers to questions asked in a 3rd party tool.
I have a bdc connection to their sql db.
I am crawling just one one table in the db.
I need to group question and answer results together using the muenchian method using several columns in the same table.
I cant change the db so I have to pull the results together in search to be something meaningful.
This table contains all the entries of questions asked and answers given.
I have to tie them together for us using search as the application to present the Q&A
overview of the data:
Each record returned in the core search results has an:
title - this is the question or the answer
activityid(int) - unique id of the entry
activitytypeid(int) - these identify if its a Q or A / 79 or 82
parentid(int) - this value contains the activityid of the original question - if the record is the original question, it will have a value of 0 for this column
isanswer(boolean) true or nothing (this is sort of redundant and does the same as activitytypeid but I was not sure if I could use it to help with the grouping)
Review
activitype id is either:
79 - question being asked - this will have a parentid of 0
or
82 - answer to question - this will have a parent id matching the activityid of the original question
I am trying but without much success to get my search results grouped/sorted to have the question returned first. In the example below it would be activityid 19142 and the answers to the question indented underneath with all grouped in a set
any help in reviewing my hack at this or examples would be great.
I am just trying to understand xsl and this has been a big learning curve. I have a long way to go and most certainly have jumped into the deep end on this.
raw xml of results i currently get without formatting the results
<All_Results>
<Result>
<id>1</id>
<title>Original Question</title>
<isanswer></isanswer>
<parentid>0</parentid>
<activitytype>79</activitytype>
<activityid>19142</activityid>
</Result>
<Result>
<id>2</id>
<title>Answer to original question</title>
<isanswer>true</isanswer>
<parentid>19142</parentid>
<activitytype>82</activitytype>
<activityid>19146</activityid>
</Result>
<Result>
<id>3</id>
<title>Another Question</title>
<isanswer></isanswer>
<parentid>0</parentid>
<activitytype>79</activitytype>
<activityid>19200</activityid>
<Result>
<Result>
<id>4</id>
<title>Second answer to original question</title>
<isanswer>true</isanswer>
<parentid>19142</parentid>
<activitytype>82</activitytype>
<activityid>19199</activityid>
</Result>
<Result>
<id>5</id>
<title>and another Question</title>
<isanswer></isanswer>
<parentid>0</parentid>
<activitytype>79</activitytype>
<activityid>19254</activityid>
<Result>
<Result>
<id>6</id>
<title>Answer to another question</title>
<isanswer>true</isanswer>
<parentid>19200</parentid>
<activitytype>82</activitytype>
<activityid>19265</activityid>
</Result>
</All_Results>
The output I am looking for would have all answers grouped with the corresponding question this is very simplified but I think it get s the point I am trying to get too.
<All_Results>
<Result>
<title>Original Question</title>
<title>Answer to original question</title>
<title>Second answer to original question</title>
</Result>
<Result>
<title>Another Question</title>
<title>Answer to another question</title>
</Result>
</All_Results>
my attempts at the template
<xsl:template match="All_Results">
<xsl:for-each select="Result[count(. | key('QAsked', 'activityid')[1]) > 0]">
<xsl:sort select="questionasked" />
<xsl:value-of select="title" /><br />
<xsl:value-of select="questionasked" /><br />
<xsl:value-of select="activityid" /><br /><br />
<xsl:for-each select="key('QAsked', '82')">
<xsl:sort select="questionasked" />
<xsl:if test="normalize-space(questionasked) = '82'"></xsl:if><br />
<xsl:value-of select="questionasked" /><br />
<xsl:value-of select="originalquestion" /><br />
<xsl:value-of select="likes" /><br /><br />
</xsl:for-each>
</xsl:for-each>
</xsl:template>
Try this template:
<xsl:key name="QAsked" match="Result[(activitytype = '82') or (activitytype = '79')]" use="concat(substring(parentid, 1, number(not(parentid = '0')) * string-length(parentid)),substring(activityid, 1, number(parentid = '0') * string-length(activityid)))"/>
<xsl:template match="All_Results">
<All_Results>
<xsl:for-each select="Result[count(. | key('QAsked', concat(substring(parentid, 1, number(not(parentid = '0')) * string-length(parentid)),substring(activityid, 1, number(parentid = '0') * string-length(activityid))))[1]) = 1]">
<xsl:sort select="id" />
<Result>
<xsl:for-each select="key('QAsked', concat(substring(parentid, 1, number(not(parentid = '0')) * string-length(parentid)),substring(activityid, 1, number(parentid = '0') * string-length(activityid))))">
<xsl:sort select="parentid" />
<title>
<xsl:value-of select="title" />
</title>
</xsl:for-each>
</Result>
</xsl:for-each>
</All_Results>
</xsl:template>
The output is xml structure which you provided as an example of the desired result.
I am processing this XML:
<Brand>
<Brand_Name>BLENDERM</Brand_Name>
<Brand_Code>1103</Brand_Code>
<Groups>
<Group>
<Group_Code>657</Group_Code>
<Parent_Code>0</Parent_Code>
<Group_Level>1</Group_Level>
<Group_Name>Brand Default</Group_Name>
<Product>
<Pip_code>0032359</Pip_code>
<Status>In Use</Status>
Using this XSLT:
<xsl:template match="Product" mode="phase-3">
<xsl:value-of select="document('rx_catmapping.xml')/descendant::mapping[source=substring(ancestor::Brand/Brand_Name,1,1)]/target"/>
</xsl:template>
Here is a sample of rx_catmapping.xml:
<Lookup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<mapping>
<source>a</source>
<target>788</target>
</mapping>
<mapping>
<source>B</source>
<target>789</target>
</mapping>
</Lookup>
So, I am processing the Product element, which is a descendant of Brand. The first letter of Brand/Brand_Name in this case is B, and I am trying to output the value 789 by looking it up in rx_catmapping.xml. This should be really simple but I am completely stumped! I have tried changing the first part of the XPath to refer to document('rx_catmapping.xml')/Lookup/mapping, or document('rx_catmapping.xml')//mapping. I have also tried changing the first half of the comparison to string(source), or to source/text(), but neither of these works either. (The reason for trying this was that using source='B', for example, did seem to work, so I wondered if I was trying to compare two incompatible data types.)
Thanks in advance for your help.
Define a key
<xsl:key name="k1" match="mapping" use="source"/>
then use
<xsl:variable name="map-doc" select="document('rx_catmapping.xml')"/>
and
<xsl:variable name="letter" select="substring(ancestor::Brand/Brand_Name,1,1)"/>
<xsl:for-each select="$map-doc">
<xsl:value-of select="key('k1', $letter)/target"/>
</xsl:for-each>
With XSLT 2.0 you can simplify that to
<xsl:value-of select="key('k1', substring(ancestor::Brand/Brand_Name,1,1), $map-doc)"/>
The problem is, I believe, that at the point where you do ancestor::Brand/Brand_Name, the context is the mapping element in the external file. This worked for me
<xsl:template match="/">
<xsl:apply-templates select=".//Product"/>
</xsl:template>
<xsl:template match="Product">
<xsl:variable name="x" select="substring(ancestor::Brand/Brand_Name,1,1)"/>
<xsl:value-of select="document('file:///c:/temp/rx_catmapping.xml')//mapping[source=$x]/target"/>
</xsl:template>
I'm using a "choice" site column with the multiple-check option enabled so that users can tag a list item with several choices from the column.
This column then powers a design feature in a content query webpart - where the column choice is appended to create an image filename.
Choice1
Choice2
Choice3
Choice4
becomes
<img src="http://mysite/content-Choice1.jpg />
The problem I've got is that the XSL parser is fed a string which has semicolons (;) and hashes (#) separating the choice values. If all 4 options were ticked, the string fed into the XSLT parser would be:
;#Choice1;#Choice2;#Choice3;#Choice4
How can I work through the string and separate each choice into its own XSL variable?
I've tried various substring-before functions, but I can't get anything working.
Since XPath 1.0 does not support the tokenize() function, you'll have to do all the work yourself. For instance, you can generate the <img> elements recursively from the choices:
<xsl:template name="RecurseConvertChoicesToImages">
<xsl:param name="choices" />
<xsl:variable name="token"
select="substring-before($choices, ';#')" />
<xsl:variable name="nextToken"
select="substring-after($choices, ';#')" />
<xsl:if test="$token">
<img src="http://mysite/content-{$token}.jpg" />
</xsl:if>
<xsl:if test="$nextToken">
<xsl:call-template name="RecurseConvertChoicesToImages">
<xsl:with-param name="choices" select="$nextToken" />
</xsl:call-template>
</xsl:if>
</xsl:template>
I would recommend to use JavaScript to parse the string and accordingly display the image using JavaScript.