Convert Flexform XML string from database to array, convert array to Flexform XML string to write to database in TYPO3 - typo3

Question: How to convert Flexform XML to a data format where settings can be migrated and then write it back to DB. Are there some pitfalls or things I have to consider? Is there any reason not to just use GeneralUtility::xml2array() and GeneralUtility::array2Xml?
TYPO3 v11 / v12.
More details:
This is a common use case for me in my extensions: Often the schema of the Flexform is changed to improve usability. For the sake of the editors I want to migrate existing values to the new fields.
This would be pretty straightforward (in an Upgrade Wizard or Command):
Get the XML string representation from $row['pi_flexform']
Convert it to an array
Make the changes in the array
Convert it again to an XML string representation
If this is different from previous, write to $row['pi_flexform'].
The problem is figuring out which TYPO3 functions to use. There is FlexFormService, FlexFormTools and there is GeneralUtility::xml2array() and GeneralUtility::array2Xml.
The simplest solution seems to be to use array2xml and xml2array.
The second problem is figuring out what is valid XML for Flexform (unless the functions already take care of that).
Let's say I had something like this in pi_flexform which should be converted:
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<T3FlexForms>
<data>
<sheet index="sDEF">
<language index="lDEF">
<field index="settings.foo">
<value index="vDEF">content of foo</value>
</field>
...
I don't really need the language index but that is how it looks now.
The foo field is gone in the new Flexform and the content should be converted to:
....
<field index="settings.bar">
<value index="vDEF">content of foo</value>
</field>
...
The real use case is more complicated and actually makes sense ;-)
Unfortunately, the API is not so clear.
use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
use TYPO3\CMS\Core\Service\FlexFormService;
Convert XML to array: Solution 1
$xml = $this->flexformTools->cleanFlexFormXML('tt_content', 'pi_flexform', $row);
$xmlArray = $this->flexformTools->cleanFlexFormXML;
Result: If the Flexform schema only contains the new fields, the old fields are removed - so we can't use this if the schema changes
Convert XML to array: Solution 2
$xml = $row['pi_flexform'];
$this->flexformService->convertFlexFormContentToArray($xml);
Result: This works, but the resulting $xmlArray looks like this ['settings' => ['foo' => ...
can be used for flexforms with settings but probably not best choice if is to be written again
Convert XML to array:
solution 3
$xml = $row['pi_flexform'];
$xmlArray = \TYPO3\CMS\Core\Utility\GeneralUtility::xml2array($xml)
Unclear: what should be in second and third parameter?
the result actually looks good: ['data' => ['sDEF' => ['lDef' => ['settings.foo]
Write array to XML: Solution A
$xml = $this->flexformTools->flexArray2Xml($xmlArrray, true);
Write array to XML: Solution B
$xml = GeneralUtility::array2xml($xmlArrray);
Solution A would write it like this (if combined with convertFlexFormContentToArray). Apart from that they should both work.
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<T3FlexForms>
<settings>
<link>http://csd.informatik.uni-oldenburg.de/adam/adamMC.bib</link>
<sort>none</sort>
<sortfixed>0</sortfixed>
<filterType>none</filterType>
<filterEntries></filterEntries>
</settings>
</T3FlexForms>

Related

Extract Key from XML

I am invoking an API using "Invoke-WebRequest" and getting the below as return XML.
<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://www.sft.com/">a879-956c3452f55e3c</string>
How can I extract the "a879-956c3452f55e3c" part from this retun XML.
I tried this code
$tmpstring1 = $temp.Content -split 'com/">'
$finalString1 = $tmpstring1[1] -split "</"
$Key = $finalString1[0]
String parsing of XML text is best avoided, because it is brittle; it's always preferable to us a dedicated XML parser; fortunately, PowerShell provides easy access to .NET's System.Xml.XmlDocument type ([xml]):
$xmlText = #'
<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://www.sft.com/">a879-956c3452f55e3c</string>
'#
([xml] $xmlText).string.InnerText # .'#text' works too
Note: PowerShell conveniently adapts the XML DOM for dot notation, so that elements and attributes can be accessed as if they were regular object properties - see this answer for more information.
Normally, an XML element that only has text content (a text child node, such as the <string> element's a879-956c3452f55e3c value here) directly returns that text content when accessed with dot notation (.string).
However, because the <string> element has a namespace declaration (xmlns=...), .string actually returns a [System.Xml.XmlElement] instance whose text child node must explicitly be accessed, either via its .InnerText property or via the adapted property named for the (generic) node name of the text child element, .'#text'.

JasperReport Studio / JRXML / JasperReports number format of table element

following problem in JasperReport Studio:
I designed a query, I designed, inserted a table element in the Report but when I preview the data numbers look like this:
3.0083728739827928739279
I would like them to look like this:
3.01
In a element I can just add a pattern. For example:
<textField pattern="#,##0.###">
This (i.e.: jr:table pattern="#,##0.###") does not work with a table element.
So how do I format a table?
Thanks and best regards, Joachim
edit 25/07/2018:
one solution is to add the pattern to each textField -element within the jr:table -element by editing the JRMXL code, still I would like to know how to reach this setting via a GUI (i.e. JasperReport Studio)
Set the Pattern using Java decimal Formate.
Example
<field name="data" class="java.lang.Double"/>
"<textFieldExpression><![CDATA[new DecimalFormat("0.0").format($F{data})]]></textFieldExpression>"
I hope Its properly work for you.

How to provide field type in XML datasource of ireport designer?

I am newbie to the ireport designer.This question may simple for you.I have tried to XML file as data source and it worked.The problem is All the fields are coming as java.lang.string. How to provide the field type in XML datasource.Is it possible to provide field type in XML file itself. Consider
<customer>
<name>obuli</name>
<age><22></age>
<subscriber>
<name>sundar</name>
<no_of_transactions>100</no_of_transactions>
</subscriber>
</customer>
Here the customer and subscriber are classes so i need to provide filed type com.test.Customer,com.test.Subscriber. I can achieve this by java bean datasource. But still i need the XML datasource way.
Is it possible to set the field type in XML datasource way ?
I do not think so. I also do not think you need that feature as reports only require scalar values. So just map the scalar values in your datasource to your scalar valued report fields.
You have to go on the XML code of your document and then search your field tag name, for example:
<field name="xxxx" class="java.lang.String"/>
change to Date type:
<field name="xxxx" class="java.util.Date"/>

XSLT - how to put original XML into transformation result in text output mode

i am trying to create a transformation which output will be text but including original xml as well. Simply i got the xml message that should be transformed to SQL insert but in case of an SQL error i want to insert the original xml message to database as well.
The input is e.g.:
<message><tag name="foo">dummy</tag></message>
The result of the transformation should be then:
INSERT INTO table (column) VALUES ('dummy')
IF ##error <> 0
BEGIN
INSERT INTO errMsgLog (message) VALUES ('<message><tag name="foo">dummy</tag></message>')
END
The problem is if i set the output in XSLT to 'text' there are no xml tags included (just the values). So is there any mixed output mode or attribute override?
Thanks for any help.
Before approaching this solution (don't know if through XSLT you can find some better solution), also consider the problems you will encounter with much more complex input and output.
Even if purists will reject this answer (and the question also), you can use "xml" output method to do a (very ugly) trickery:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:text disable-output-escaping="yes">INSERT INTO table (column) VALUES ('dummy')
IF ##error <> 0
BEGIN
INSERT INTO errMsgLog (message) VALUES ('</xsl:text>
<xsl:copy-of select="."/><xsl:text>')
END</xsl:text>
</xsl:template>
</xsl:stylesheet>
outputs:
INSERT INTO table (column) VALUES ('dummy')
IF ##error <> 0
BEGIN
INSERT INTO errMsgLog (message) VALUES ('<message><tag name="foo">dummy</tag></message>')
END
Some processors (e.g. Saxon) have an extension function serialize() which allows you to convert an XML node into a serialised XML representation, which the function returns as a string. You could call this and then output it in your text result. If your processor doesn't have such an extension function, then it might not be difficult to write one.

iPhone : parse Lengthy XML file on the Base of Key Field

i want to parse the xml File. xml File structure is following
<?xml version="1.0" encoding="utf-8"?>
<Level>
<p id='327'>
<Item>
<Id>5877</Id>
<Type>0</Type>
<Icon>---</Icon>
<Title>Btn1Item1</Title>
</Item>
<Item>
<Id>5925</Id>
<Type>0</Type>
<Icon>---</Icon>
<Title>Btn1Item4</Title>
</Item>
</p>
<p id='328'>
<Item>
<Id>5878</Id>
<Type>0</Type>
<Icon>---</Icon>
<Title>Btn2Item1</Title>
</Item>
<Item>
<Id>5926</Id>
<Type>0</Type>
<Icon>---</Icon>
<Title>Btn2Item4</Title>
</Item>
</p>
</Level>
in above code there are only 2 tag for <p>. but in actual there are multiple tag. i want to search the specific tag for which attribute id have some specific value (say 327).
so one way is that i parse the XML file from start to get the desired result. whether there are any other method from which i can direct locate the desired tag. for example if i want to search the <p> tag in above XML for attribute id =328, then it does not parse the id=327 and direct return only those item which are related to id=328
Please suggest
Depends how you define "parse".
A "quick & dirty" (and potentially buggy) way would be to find the fragment using a regex search (or a custom parser) first, then feed the fragment to a true XML parser. I don't know of anything that would do this for you, you'd have to roll it yourself. I would suggest that it's not the way to go.
The next level is to feed it through a SAX-like parser (which NSXMLParser is a form of).
In your handler for the <p> element, check the id attribute and if it matches your value (or values), set a flag to indicate if child elements should be interpreted.
In your child element handlers, just check that flag first (in a raw NSXMLParser handler all elements would go to the same method, of course).
So it's true that NSXMLParser would be parsing the whole document - but just to do the minimal work to establish the correct XML parser context. The real work of handling the elements would be deferred until the value is met. I don't see any way around that without something hacky like the regex suggestion.
If this is too much overhead I'd reconsider whether XML is the right serialization format for you (assuming you have any control over that)?
If you do stick with NSXMLParser, my blog article here might help to at least make the experience nicer.
The libxml2 library can receive XPath queries with the following extensions. With these extensions you might issue the XPath query /p[#id = "328"] to retrieve that specific node's children.