I'm currently doing some parsing of very large xml files > 40 MB. I have just started developing in scala so I browsed the net for some good libs and stumbled upon Scala Scales which seems to be very good at handling large files.
I have read:
http://scala-scales.googlecode.com/svn/sites/scales/scales-xml_2.9.1/0.2/ScalesXmlIntro.html
,
http://scala-scales.googlecode.com/svn/sites/scales/scales-xml_2.9.2/0.4.4/PullParsing.html
and then tested the pullXml function, to make sure all libs are imported correctly.
val pull = pullXml(new FileReader("/Users/mycrazyxml/tmp/large.xml"))
while( pull.hasNext ){
pull.next match {
case Left( i : XmlItem ) =>
// Handle XmlItem
Logger.info("XmlItem: "+i)
case Left( e : Elem ) => {
// Handle Element
Logger.info("Element: "+e)
}
case Right(endElem) =>
// Handle endElement
Logger.info("Endelement: "+endElem)
}
}
This results in that the entire file is printed to the console! Nice!
Now it's time create the objects and save to the db, but I'm having
trouble in grasping how to do this in a good way. I would really need some good examples
of how to do this.
Eg. following XML has several Enterprise elements which can consist of one or several LocalUnits.
The idea here is to create an Enterprise object with an array of LocalUnits. When
the endElement is the closing tag for an Enterprise call the save method with the Enterprise object with it's LocalUnits.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Info SYSTEM "info.dtd">
<Info>
<Enterprise>
<RegNo>12345678</RegNo>
<Address>
<StreetInfo>
<StreetName>Infinite Loop</StreetName>
<StreetNumber>1</StreetNumber>
</StreetInfo>
</Address>
<EName>
<Legal>Crazy Company</Legal>
</EName>
<SNI>
<Code>00000</Code>
<Rank>1</Rank>
</SNI>
<LocalUnit>
<CFARNo>987654321</CFARNo>
<LUType>1</LUType>
<LUName>Crazy Company Gym</LUName>
<LUStatus>1</LUStatus>
<SNI>
<Code>46772</Code>
<Rank>1</Rank>
</SNI>
<SNI>
<Code>68203</Code>
<Rank>2</Rank>
</SNI>
<Address>
<StreetInfo>
<StreetName>Infinite Loop</StreetName>
<StreetNumber>1</StreetNumber>
</StreetInfo>
</Address>
</LocalUnit>
<LocalUnit>
<CFARNo>987654322</CFARNo>
<LUType>1</LUType>
<LUName>Crazy Company Restaurant</LUName>
<LUStatus>1</LUStatus>
<SNI>
<Code>46772</Code>
<Rank>1</Rank>
</SNI>
<SNI>
<Code>68203</Code>
<Rank>2</Rank>
</SNI>
<Address>
<StreetInfo>
<StreetName>Infinite Loop</StreetName>
<StreetNumber>1</StreetNumber>
</StreetInfo>
</Address>
</LocalUnit>
</Enterprise>
<Enterprise>
<RegNo>12345671220</RegNo>
<Address>
<StreetInfo>
<StreetName>Cupertino Road</StreetName>
<StreetNumber>2</StreetNumber>
</StreetInfo>
</Address>
<EName>
<Legal>Fun Company HQ</Legal>
</EName>
<SNI>
<Code>00000</Code>
<Rank>1</Rank>
</SNI>
<LocalUnit>
<CFARNo>987654321</CFARNo>
<LUType>1</LUType>
<LUName>Fun Company</LUName>
<LUStatus>1</LUStatus>
<SNI>
<Code>46772</Code>
<Rank>1</Rank>
</SNI>
<SNI>
<Code>68203</Code>
<Rank>2</Rank>
</SNI>
<Address>
<StreetInfo>
<StreetName>Cupertino road</StreetName>
<StreetNumber>2</StreetNumber>
</StreetInfo>
</Address>
</LocalUnit>
</Enterprise>
</Info>
To sum it up. For the given xml how should I use pullXml to create my objects and call the save method with them?
val xmlFile = resource(this, "/data/enterprise_info.xml")
val xml = pullXml(xmlFile)
val Info = NoNamespaceQName("Info")
val Enterprise = NoNamespaceQName("Enterprise")
val LocalUnit = NoNamespaceQName("LocalUnit")
val LocalUnitName = NoNamespaceQName("LUName")
val EName = NoNamespaceQName("EName")
val Legal = NoNamespaceQName("Legal")
val EnterprisePath = List(Info, Enterprise)
// iterate over each Enterprise
// only an Enterprise at a time is in memory
val itr = iterate(EnterprisePath, xml)
for {
enterprise <- itr
enterpriseName <- enterprise \* EName \* Legal
} {
println("enterprise "+text(enterpriseName) +" has units:")
for {
localUnits <- enterprise \* LocalUnit
localName <- localUnits \* LocalUnitName
}{
println(" " + text(localName))
}
//do a save
}
Pulling in each LocalUnit lazily is more difficult at the moment, you must separate Paths for each subsection which isn't a LocalUnit.
Hth
Maybe this could help you? I think using small snippets of xml works very well in Scala.
http://joncook.github.io/blog/2013/11/03/xml-processing-with-scala/
Related
I have an issue on using SimpleRemoteObject. (sdk 0.9.6)
My actual website is using this code to call remote function with Amfphp :
<mx:RemoteObject id="ro" source="aadmin" destination="amfphp">
<mx:method name="siteLogin" fault="{onRcv_siteLoginErr(event)}" result="{onRcv_siteLogin(event)}"/>
</mx:RemoteObject>
As <mx:method/> does't exist in Apache Royale I set this code :
</js:beads>
<js:SimpleRemoteObject id="sro" source="aadmin" result="onResult(event)" fault="onFault(event)"
endPoint = "http://amfphp.myserver_url.com/gateway.php"
destination = "amfphp" />
</js:beads>
aadmin is my php class service name
To call my function I do :
sro.send("siteLogin",["123"]);
where siteLogin is my function to call inside aadmin class
Running this, I have this issue :
The class {Amf3Broker} could not be found under the class path {/home/www/amfphp/services/amfphp/Amf3Broker.php}
Why does it show Amf3Broker ? Does anyone have an exemple of working SimpleRemoteObject with amfphp ?
Server side I use https://github.com/silexlabs/amfphp-1.9
Do I need to setup a service-config.xml file ? If yes how to use it with compiler ? (I tried "services": "services-config.xml" in compilerOptions but not working)
Here is my service-config.xml :
<services-config>
<services>
<service id="amfphp-flashremoting-service" class="flex.messaging.services.RemotingService" messageTypes="flex.messaging.messages.RemotingMessage">
<destination id="amfphp">
<channels>
<channel ref="my-amfphp"/>
</channels>
<properties>
<source>*</source>
</properties>
</destination>
</service>
</services>
<channels>
<channel-definition id="my-amfphp" class="mx.messaging.channels.AMFChannel">
<endpoint uri="http://amfphp.myserver.com/gateway.php" class="flex.messaging.endpoints.AMFEndpoint"/>
<properties><add-no-cache-headers>false</add-no-cache-headers></properties>
</channel-definition>
</channels>
</services-config>
Now I have do a test with amfphp V2.0 from https://github.com/silexlabs/amfphp-2.0
This is a little better, but I have an error. It seem that there is an issue with _explicitType property. Moreover I don't see my argument ('123') in [requestMessage]
/onStatusî$flex.messaging.messages.ErrorMessage
correlationId faultCode# faultDetailfaultStringvUndefined property: stdClass::$_explicitType .
<br>file: /home/www/mysite.com/amfphpv2/Plugins/AmfphpFlexMessaging/AmfphpFlexMessaging.php
<br>line: 113
<br>context: Array
(
[requestMessage] => Amfphp_Core_Amf_Message Object
(
[targetUri] => null
[responseUri] => /1
[data] => Array
(
[0] => stdClass Object
(
[body] => stdClass Object
(
)
[clientId] =>
[correlationId] =>
[destination] => amfphp
[headers] => stdClass Object
(
)
[messageId] => EF4BF9E3-5C02-1060-1FF3-5D9781F55A31
[operation] => 13
[timeToLive] => 0
[timestamp] => 0
)
)
)
[serviceRouter] => Amfphp_Core_Common_ServiceRouter Object
(
[serviceFolders] => Array
(
[0] => /home/www/mysite.com/amfphpv2/Core/../Services/
)
[serviceNames2ClassFindInfo] => Array
(
[AmfphpMonitorService] => Amfphp_Core_Common_ClassFindInfo Object
(
[absolutePath] => /home/www/mysite.com/amfphpv2/Plugins/AmfphpMonitor/AmfphpMonitorService.php
[className] => AmfphpMonitorService
)
[AmfphpDiscoveryService] => Amfphp_Core_Common_ClassFindInfo Object
(
[absolutePath] => /home/www/mysite.com/amfphpv2/Plugins/AmfphpDiscovery/AmfphpDiscoveryService.php
[className] => AmfphpDiscoveryService
)
)
[checkArgumentCount] => 1
)
[explicitTypeField] => _explicitType
)
rootCause
Thanks in advance for any help...
Here is some tested working code on 0.9.6 sdk (please notice that you must use config flex to be able to use mx if you have mx library issue). tested with v1.9 and v2.0 AMFPHP from silexlabs :
<fx:Declarations>
<mx:RemoteObject id="ro" result="onResult(event)" fault="onFault(event)" source="your-service-php-class"
endpoint = "https://www.your-amfphp-server.com/amfphp/gateway.php"
destination = "amfphp" />
</fx:Declarations>
Then in script
ro.getOperation("your-php-function-to-call").send("your-param");
[update]
Important : be sure to have this in your application else you will have error like *The class {Amf3Broker} could not be found*
<mx:beads>
<js:ClassAliasBead />
</mx:beads>
[update#2]
You what to use config royale but also wants MX libs to use MX remote object ? Here is how :
https://github.com/apache/royale-asjs/issues/495#issuecomment-539906300
the backends I know are working (from my own experience) are Java and .NET(Fluorine). AMFPHP must work too. Others tried it but was almost a year ago when AMF was not completely developed. Right now AMF in Royale is very robust and works very good with all types with the exception of Vector and Dictionary (I suppose those will come some day but since are AS3 types, has lower priority for now).
The main thing here is to use MXRoyale version of RemoteObject (mx:RemoteObject emulation) since this one is the most closest to Flex RemoteObject. The others in Network lib are more lighter classes implemented as beads that were the first ones to come to Royale. But at least in my case I switched to mx:RemoteObject, so I can ensure the others are working at the same level.
Is it a bug or per design that xmlns attribute is not ignored?
(cake version 0.33.0)
With an Xml like so (a too simplified nuspec file):
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<!-- Continuously updated elements -->
<version>3.0.0</version>
</metadata>
</package>
I do a naÏve call
var x = XmlPeek( "my.nuspec", "/package/metadata/version/text()" );
ad get the result x==null.
So I specify the namespace like so:
var settings = new XmlPeekSettings{
Namespaces = new Dictionary<string, string> {{
"ps", "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"
}}
};
var x = XmlPeek( "my.nuspec", "/ps:package/ps:metadata/ps:version/text()", settings);
and get the result x==3.0.0 I anticipated.
It is not a bug.
To ignore the namespace you can use namespace agnostic xpath such as local-name():
var x = XmlPeek( "my.nuspec", "/*[local-name() = 'package']/*[local-name() = 'metadata']/*[local-name() = 'version']/text()");
or if you have only one version node:
var x = XmlPeek( "my.nuspec", "//*[local-name()='version']/text()");
but be careful with documents with large numbers of elements - this can become very slow.
I want to make a post request in Scala to this API http://api.atinternet-solutions.com/toolbox/reporting.asmx
I first did it with a curl like this :
curl -X POST -T post.txt -H "Content-Type: application/soap+xml; charset=utf-8" http://api.atinternet-solutions.com/toolbox/reporting.asmx -v
and I got what I expected.
Now I want to call the API programatically with a simple HttpClient
val httpClient = new DefaultHttpClient()
def postRequest=new HttpPost("https://api.atinternet-solutions.com/toolbox/reporting.asmx")
postRequest.addHeader("Content-Type","application/soap+xml ; charset=utf-8")
postRequest.addHeader("SOAPAction","\"http://www.xiti.com/queryReport\"")
val file=new File("post.txt")
val fe=new FileEntity(file,"application/soap+xml;charset=utf-8")
postRequest.setEntity(fe)
val httpResponse=httpClient.execute(postRequest)
println(httpResponse.getStatusLine.toString)
val rspStr=Source.createBufferedSource(httpResponse.getEntity.getContent).mkString
println(rspStr)
but I get an HTTP/1.1 500 Internal Server Error
and printing the rspStr yields
"?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><soap:Fault><soap:Code><soap:Value>soap:Receiver</soap:Value></soap:Code><soap:Reason><soap:Text xml:lang="en">Server was unable to process request. ---> Root element is missing.</soap:Text></soap:Reason><soap:Detail /></soap:Fault></soap:Body></soap:Envelope"
The post.txt looks like below, except I set the good information instead.
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Header>
<NXHeader xmlns="http://www.xiti.com/">
<GUID>string</GUID>
<SecurityKey>string</SecurityKey>
<Site>int</Site>
</NXHeader>
</soap12:Header>
<soap12:Body>
<queryReport xmlns="http://www.xiti.com/">
<startDate>int</startDate>
<endDate>int</endDate>
<query>string</query>
<param>string</param>
<typereport>XML or CSV</typereport>
</queryReport>
</soap12:Body>
</soap12:Envelope>
The curl action which is working is done from my scala project directory. I printed post.txt in scala and got the good content.
I cannot figure out what is going wrong. Thank you for your help
I tried your call with the dispatch library.
This is my code
import dispatch._
import java.io.File
object ToolBox {
val endpoint = url("https://api.atinternet-solutions.com/toolbox/reporting.asmx").POST
.addHeader("Content-Type","application/soap+xml ; charset=utf-8")
.addHeader("SOAPAction",""" "http://www.xiti.com/queryReport" """)
def report = Http( endpoint <<< new File("post.txt") > as.xml.Elem)
def tryReport = {
val res = report.either
for {
ex <- res.left
} yield "Something got wrong " + ex.getMessage
}
}
The service replies with a status 500 like you, but the faultString in the response xml is: A parameter is missing in your NXHeader or you have a namespace issue.
Is it what you would expect, since the post.txt body contains only the placeholders from the query report example, instead of real parameters?
I found what was going wrong.
Replace
def postRequest
by
val postRequest.
Tks again
I'm fairly new to Scala and need to build a really simple command line parser which provides something like the following which I created using JRuby in a few minutes:-
java -jar demo.jar --help
Command Line Example Application
Example: java -jar demo.jar --dn "CN=Test" --nde-url "http://www.example.com" --password "password"
For usage see below:
-n http://www.example.com
-p, --password set the password
-c, --capi set add to Windows key-store
-h, --help Show this message
-v, --version Print version
Scallop looks like it will do the trick, but I can't seem to find a simple example that works! All of the examples I've found seem to be fragmented and don't work for some reason or other.
UPDATE
I found this example which works, but I'm not sure how to bind it into the actual args within the main method.
import org.rogach.scallop._;
object cmdlinetest {
def main(args: Array[String])
val opts = Scallop(List("-d","--num-limbs","1"))
.version("test 1.2.3 (c) 2012 Mr Placeholder")
.banner("""Usage: test [OPTION]... [pet-name]
|test is an awesome program, which does something funny
|Options:
|""".stripMargin)
.footer("\nFor all other tricks, consult the documentation!")
.opt[Boolean]("donkey", descr = "use donkey mode")
.opt("monkeys", default = Some(2), short = 'm')
.opt[Int]("num-limbs", 'k',
"number of libms", required = true)
.opt[List[Double]]("params")
.opt[String]("debug", hidden = true)
.props[String]('D',"some key-value pairs")
// you can add parameters a bit later
.args(List("-Dalpha=1","-D","betta=2","gamma=3", "Pigeon"))
.trailArg[String]("pet name")
.verify
println(opts.help)
}
}
Well, I'll try to add more examples :)
In this case, it would be much better to use ScallopConf:
import org.rogach.scallop._
object Main extends App {
val opts = new ScallopConf(args) {
banner("""
NDE/SCEP Certificate enrollment prototype
Example: java -jar demo.jar --dn CN=Test --nde-url http://www.example.com --password password
For usage see below:
""")
val ndeUrl = opt[String]("nde-url")
val password = opt[String]("password", descr = "set the password")
val capi = toggle("capi", prefix = "no-", descrYes = "enable adding to Windows key-store", descrNo = "disable adding to Windows key-store")
val version = opt[Boolean]("version", noshort = true, descr = "Print version")
val help = opt[Boolean]("help", noshort = true, descr = "Show this message")
}
println(opts.password())
}
It prints:
$ java -jar demo.jar --help
NDE/SCEP Certificate enrollment prototype
Example: java -jar demo.jar --dn CN=Test --nde-url http://www.example.com --password password
For usage see below:
-c, --capi enable adding to Windows key-store
--no-capi disable adding to Windows key-store
--help Show this message
-n, --nde-url <arg>
-p, --password <arg> set the password
--version Print version
Did you read the documentation? It looks like all you have to do is call get for each option you want:
def get [A] (name: String)(implicit m: Manifest[A]): Option[A]
It looks like you might need to provide the expected return type in the method call. Try something like this:
val donkey = opts.get[Boolean]("donkey")
val numLimbs = opts.get[Int]("num-limbs")
If you're just looking for a quick and dirty way to parse command line arguments, you can use pirate, an extremely barebones way to parse arguments. Here is what it would look like to handle the usage you describe above:
import com.mosesn.pirate.Pirate
object Main {
def main(commandLineArgs: Array[String]) {
val args = Pirate("[ -n string ] [ -p string ] [ -chv ]")("-n whatever -c".split(" "))
val c = args.flags.contains('c')
val v = args.flags.contains('v')
val h = args.flags.contains('h')
val n = args.strings.get("n")
val p = args.strings.get("p")
println(Seq(c, v, h, n, p))
}
}
Of course, for your program, you would pass commandLineArgs instead of "-n whatever -c".
Unfortunately, pirate does not yet support GNU style arguments, nor the version or help text options.
In my app I have to send email to recipient who has umlauts in domain name.
Example:
"test#äöü.test.com"
I'm using cfmail tag and I'm getting such error:
"invalid definition for attribute to at tag mail"
"Invalid E-Mail Address definition (test#äöü.test.com)"
Is there any way to send email to such recipients in coldfusion?
There is even a easier solution! Why not use Oracles built in class:
http://download.oracle.com/javase/6/docs/api/java/net/IDN.html#toUnicode(java.lang.String)
Then you only have to do this (example shows from punycode to Unicode):
<cfset strUrl = "xn--land-poa.se" />
<!--- Create a Java URL. --->
<cfset jUrl = CreateObject( "java", "java.net.IDN" ).toUnicode(strUrl) />
<cfoutput>
#jUrl#
You don´t have to download anything!
I'm no I18N expert but I was intrigued enough to investigate and come up with the following solution.
The problem is essentially how to send mail to Internationalised Domain Names (IDN), i.e. those which contain non-ASCII characters. IDNs are valid nowadays but not recognized by many systems including Java (and therefore ColdFusion, which uses the Java validation for CFMAIL address fields - hence the error you're seeing).
For a system to recognise an IDN it needs to be converted to an ASCII form called Punycode. For example müller.org needs to be converted to xn--mller-kva.org
LibIdn is an OS java library that will do this and the following code shows how you can hook it up to CF using Mark Mandel's JavaLoader.
<cffunction name="convertIdnToAscii" returntype="string" output="false">
<cfargument name="domain" type="string" required="true">
<cfscript>
var local = {};
// these paths assume the JavaLoader folder and the libidn-1.22.jar are in the same folder as the cfm template.
local.javaLoaderPath = "javaLoader.JavaLoader";
local.idnLibPath = ExpandPath( "libidn-1.22.jar" );
// convert the IDN lib path to an array which is what JavaLoader expects
local.libPathArray = [ local.idnLibPath ];
//load the IDN Lib
loader = CreateObject( "component",local.javaLoaderPath ).init( local.libPathArray );
// create an instance of the IDN lib
local.idn = loader.create( "gnu.inet.encoding.IDNA" ).init();
// convert the domain name
return local.idn.toASCII( arguments.domain );
</cfscript>
</cffunction>
<cffunction name="convertIdnAddress" returntype="string" output="false">
<cfargument name="address" type="string" required="true">
<cfscript>
var local = {};
local.domain = GetToken( arguments.address,2,"#" );
local.converted = convertIdnToAscii( local.domain );
return Replace( arguments.address,local.domain,local.converted );
</cfscript>
</cffunction>
<!--- Loop over a list of addresses and convert them if necessary --->
<cfset processedAddresses = []>
<cfloop list="test#äöü.test.com,test#example.com" index="address">
<cfif( NOT IsValid( "email",address ) )>
<cfset address = convertIdnAddress( address )>
</cfif>
<cfmail server="0.0.0.0" from="sender#mydomain.com" to="#address#" subject="test">Message</cfmail>
<cfset ArrayAppend( processedAddresses,address )>
</cfloop>
<cfdump var="#processedAddresses#">
This will send 2 emails (to a non-existent mailserver) and dump the converted addresses:
test#xn--4ca0bs.test.com
test#example.com
Notes:
To get the libidn jar file, download and extract the tar and look for it in the Java directory
The above assumes the libidn jar and JavaLoader package are located in the same folder as the template contain the CF code
The above should work on CF8 and above, although I've only tested it on CF9.
Be aware there's no error handling for addresses that might be invalid for reasons other than it containing an IDN.