I am working on a NSDocument based Mac app. Which imports .xml file. It's working fine for some xml files but for few having issues.
Issue is read() is modifying the data when we import file, i need to keep the original data as it is.
what do i need to do to make sure i get original xml data in the read()?
I am using below function to read the file
override func read(from data: Data, ofType typeName: String) throws {
var error:NSError? = nil
var xmlDocument1:XMLDocument? = XMLDocument()
do{
xmlDocument1 = try XMLDocument(data: data, options: XMLNode.Options(rawValue: XMLNode.Options.RawValue(Int(XMLNode.Options.nodePreserveWhitespace.rawValue))))
}catch let err as NSError{
error = err
}
if error != nil {
throw error!
}
}
and i parse xmlDocument1 to read and get all the xml information.
Issue: Doing this way swift is modifying the document, as mentioned below.
Example 1:
Original:
<iws:attr-option name="1 - Poor" />
<iws:attr-option name="2 - Needs Improvement" />
Data getting from Read(), notice the closing tags added automatically
<iws:attr-option name="1 - Poor"></iws:attr-option>
<iws:attr-option name="2 - Needs Improvement"></iws:attr-option>
Example 2:
Original:
<source>
<ph id="12" x="</span>">{12}</ph>
</source>
Data getting from Read(), notice the ">" symbol is replaced with "& gt;"
<source>
<ph id="12" x="</span>">{12}</ph>
</source>
Example 3:
I am not able to paste the code here as the special character is not even displaying here, so adding image.
left is the original and right side one is what i am getting in read(), special character is missing.
Code Sameple : (I am not sure if we can post code directly here)
https://drive.google.com/drive/folders/1WWGE7fFJPKvs5KU5f_PlwWtoqCVxTcS0?usp=sharing
Above drive we have sample xml file and code.
"DevelopingADocumentBasedApp" is the code, just open the "DocumentBasedApp.xcodeproj", run it.
3 .Once it runs, click on Menu->File->Open and open the provided xml file.
In content.swift, Keep a break point at "print(xmlDocument!)"
Here we can see the document is modified by NSDocument, and it is different from the original
Edit:
#matt Thank you for making me understand real problem, Initially i thought that i have issue with NSDocument's read(). But issues is XMLDocument() not returning exact data. I need to find a solution for that.
Reading is not changing your document.
You make an xml document, with XMLDocument(data:...). You are asking for a new valid XML document based on your original, and that is exactly what you get. The resulting structure is not a big string, like your original data; it is an elaborate node tree reflecting the structure of your XML. That node tree is identical to the structure described by your original. That fact does not affect in any way your ability to parse the document; indeed, it is why you are able to parse the document. If you think it does cause an inability to parse the document, your parsing code is wrong (but you didn't show that, so no more can be said).
Also note that your evidence for what is "in" the XML document is indirect; the XML document is a node tree, but the strings you display are the output of a secondary rendering into a string. That rendering representation is arbitrary and malleable; it obeys its own rules of formatting. (And again, you didn't show anything about how you obtain that rendering. Perhaps we are talking about your print statement?)
The point is, you seem to have to some sort of expectation about how passing into an XMLDocument and then back out of it will "round trip" your original string in such a way that the output looks just like the original. That expectation is incorrect. That's not what XMLDocument does.
And merely reading the original data into an XMLDocument did not change the data, I can promise you that.
So don't worry, be happy; as far as the validity of your XML is concerned, everything is fine, and the data you started with has not been altered in any way.
Here's a demonstration:
let xmlstring = """
<testing>
<fun whatever="thingy" />
</testing>
"""
print(xmlstring)
let xmldata = xmlstring.data(using: .utf8)!
let xml = try? XMLDocument(data: xmldata, options: [])
print("=======")
print(xml!)
The output is:
<testing>
<fun whatever="thingy" />
</testing>
=======
<?xml version="1.0"?><testing><fun whatever="thingy"></fun></testing>
As you can see, the output from the print is not the same as the input string. But it is a valid XML representation of the original string, and that's all that matters. And the original xmlstring and xmldata that I started with are, I assure you, completely untouched.
Related
I was looking at the documentation for contentsOfDirectory(at:includingPropertiesForKeys:options:)
Particularly I've been focusing on the argument includingPropertiesForKeys, which said:
An array of keys that identify the file properties that you want pre-fetched for each item in the directory. For each returned URL, the specified properties are fetched and cached in the NSURL object. For a list of keys you can specify, see Common File System Resource Keys.
Clicking on URLResourceKey led me to the Apple Documentation about it.
And I was wondering, if I passed in keys like fileResourceTypeKey, fileResourceIdentifierKey, and creationDateKey how could I access those in the returned URL list (after calling contentsOfDirectory(at:includingPropertiesForKeys:options:))?
And I was also confused by the URLResourceKey enum b/c a lot of types have similar descriptions and names to other keys like:
documentIdentifierKey vs fileResourceIdentifierKeyvs localizedNameKey vs nameKey
localizedTypeDescriptionKey vs fileResourceTypeKey vs typeIdentifierKey
the urls returned by the contentsOfDirectory(at:includingPropertiesForKeys:options:) vs pathKey
Like what would be the differences between these keys?
Basically I have a really low understanding of the file system at this point so please bear with my "simple" questions. If someone could explain what all these keys means and how I can access/use them that would be great!
First of all the URLResourceKey documentation describes the kind of the attribute information very well. For example nameKey returns always Desktop for an URL representing ~/Desktop while localizedNameKey returns the localized name Schreibtisch on a German system or Bureau on a French system. However documentIdentifierKey and fileResourceIdentifierKey are completely different attributes.
Regarding the contentsOfDirectory(at:includingPropertiesForKeys:options:) API: The keys passed in the includingPropertiesForKeys parameter tells the framework to pre-fetch the corresponding attributes while getting the contents for performance reasons. For example
let contentURLs = try fileManager.contentsOfDirectory(at: anURL, includingPropertiesForKeys: [.nameKey, .fileSizeKey], options: .skipsHiddenFiles)
To read the attributes call resourceValues(forKeys on the URL passing the same keys as in contentsOfDirectory. Then get the value with the corresponding property of the resource key. The benefit of the combination URLResourceKey / URLResourceValues is you get always the proper type from the file attributes. This avoids any type casting.
for fileURL in contentURLs {
do {
let fileAttributes = try fileURL.resourceValues(forKeys:[.nameKey, .fileSizeKey])
print(fileAttributes.name!) // is String
print(fileAttributes.fileSize!) // is Int
} catch { print(error, fileURL) }
}
I am a Scala/PlayFramework noob here, so please be easy on me :).
I am trying to create an action (serving a GET request) so that when I enter the url in the browser, the browser should download the file. So far I have this:
def sepaCreditXml() = Action {
val data: SepaCreditTransfer = invoiceService.sepaCredit()
val content: HtmlFormat.Appendable = views.html.sepacredittransfer(data)
Ok(content)
}
What it does is basically show the XML in the browser (whereas I actually want it to download the file). Also, I have two problems with it:
I am not sure if using Play's templating "views.html..." is the best idea to create an XML template. Is it good/simple enough or should I use a different solution for this?
I have found Ok.sendFile in the Play's documentation. But it needs a java.io.File. I don't know how to create a File from HtmlFormat.Appendable. I would prefer to create a file in-memory, i.e. no new File("/tmp/temporary.xml").
EDIT: Here SepaCreditTransfer is a case class holding some data. Nothing special.
I think it's quite normal for browsers to visualize XML instead of downloading it. Have you tried to use the application/force-download content type header, like this?
def sepaCreditXml() = Action {
val data: SepaCreditTransfer = invoiceService.sepaCredit()
val content: HtmlFormat.Appendable = views.html.sepacredittransfer(data)
Ok(content).withHeaders("Content-Type" -> "application/force-download")
}
I have the following code where I am reading a file and replacing any occurences of "*.tar.gz" file with the new file name provided. Everything works fine and I can see the replaced changes in the console however I am not being able to write a new file with all the changes.
def modifyFile(newFileName: String, filename: String) = {
Source.fromFile(filename).getLines.foreach { line =>
println(line.replaceAll(".+\\.tar\\.gz", newFileName.concat(".tar.gz")))
}
}
}
You forgot to write your modified lines into the new file:
def modifyFile(newFileName: String, sourceFilePath: String, targetFilePath:String) {
scala.tools.nsc.io.File(targetFilePath).printlnAll(
Source.fromFile(sourceFilePath).getLines().map {
_.replaceAll(".+\\.tar\\.gz", newFileName.concat(".tar.gz"))
}.toSeq:_*)
}
Please note that this approach is not the most efficient in terms of performance, as the content of source file is read fully to memory, processed and then written back. More efficient approach will be more verbose and will include java's FileReader/FileWriter.
Upd
As rightfully pointed in comments you have to chose suitable way to write result to file depending on what tools and dependencies you have.
Using WinJS, while looping through a directory, how to retrieve only images in that particular directory and ignoring any other file extension, including the DoubleDots .. and the SingleDot . etc?
Something like:
var dir = Windows.Storage.KnownFolders.picturesLibrary;
dir.getFilesAsync().done(function (filesFound) {
for(var i=0; i < filesFound.length; i++){}
if(filesFound[i] IS_REALLY_AN_IMAGE_(jpeg,jpg,png,gif Only)){
//Retrieve it now!
}else{
//Escape it.
}
}})
Instead of trying to process pathnames, it will work much better to use a file query, which lets the file system do the search/filtering for you. A query also allows you to listen for the query's contentschanged event if you want to dynamically track the folder contents rather than explicitly enumerating again.
A query is created via StorageFolder.createFileQuery, createFolderQuery, or other variants. In your particular case, where you want to filter by file types, you can use createFileQueryWithOptions. This function takes a QueryOptions object which you can initialize with an array of file types. For example:
var picturesLibrary = Windows.Storage.KnownFolders.picturesLibrary;
var options = new Windows.Storage.Search.QueryOptions(
Windows.Storage.Search.CommonFileQuery.orderByName, [".jpg", ".jpeg", ".png", ".gif"]);
//Could also use orderByDate instead of orderByName
if (picturesLibrary.areQueryOptionsSupported(options)) {
var query = picturesLibrary.createFileQueryWithOptions(options);
showResults(query.getFilesAsync());
}
where showResults is some function that takes the promise from query.getFilesAsync and iterates as needed.
I go into this subject at length in Chapter 11 of my free ebook, Programming Windows Store Apps with HTML, CSS, and JavaScript, 2nd Edition, in the section "Folders and Folder Queries". Also refer to the Programmatic file search sample, as I do in the book.
When you want to display the image files, be sure to use thumbnails instead of loading the whole image (images are typically much larger than a display). That is, for each StorageFile, call its getThumbnailAsync or getScaledImageAsThumbnailAsync method. Pass the resulting thumbnail (blob) to URL.createObjectURL which returns a URL you can assign to an img.src attribute. Or you can use a WinJS.UI.ListView control, but that's another topic altogether (see Chapter 7 of my book).
I've been looking on forums for 2 days now and can't find a good answer so I'll just post it.
I appear to be having a problem posting JSON back to the controller to save. The JSON should map to model view but it keeps getting default(constructor)values rather then the values from the POST.
We have a series of JS widgets that contain a data field with json in them. We do all our data manipulation in these widget objects on the client side. When a user wants to save we grab the data we need from the widgets involved and we put it into another JSON object that matches a ViewModel and POST that back to the server.
For example:
$("#Save").click(function () {
if (itemDetails.preparedForSubmit() && itemConnections.preparedForSubmit()) {
itemComposite.data.Details = itemDetails.data;
itemComposite.data.Connections= itemConnections.data;
$.post(MYURL, itemComposite.data);
} else {
alert("failed to save");
}
});
The preparedForSubmit() method simple does stuff like any validation checks or last minute formatting you might need to do client side.
The itemDetails widgets data matches a ViewModel.
The itemConnections widgets data matches a collection of ViewModels.
The Controller looks like this:
[HttpPost]
virtual public JsonResult SaveItemDetailsComposite(ItemComposite inItemData)
{
if (ModelState.IsValid)
{
try
{
_Mapper.Save(itemComposite.Details , itemComposite.Connections);
return Json(true);
}
catch (Exception ex)
{
_log.Error("Exception " + ex.InnerException.Message);
throw;
}
}
return Json(SiteMasterUtilities.CreateValidationErrorResponse(ModelState));
}
The ItemComposite Class is a simple View Model that contains a single itemDetails object and a collection of itemConnections. When it returns data to here it is just getting the default data as if it got a new ItemComposite rather than converting the POST data.
in Firebug I see the data is posted. Although it looks weird not automatically formatted in firebug.
Are you saying that itemComposite.data is formatted as a JSON object? If so, I'm pretty sure you're going to have to de-serialize it before you can cast it to your object. Something like:
ItemComposite ic = jsSerializer.Deserialize<ItemComposite>(this.HttpContext.Request.Params[0]);
You may want to look into a framework like JSON.NET to ensure that your data is being serialized properly when it gets supplied to your Action.
JSON.NET seems like it's one of the main stream frameworks: http://json.codeplex.com/releases/view/43775
Hope this helps.
Cory
You could also use the JSON Serializer in WCF: http://msdn.microsoft.com/en-us/library/system.runtime.serialization.json.datacontractjsonserializer.aspx
SO wouldn't let me put both links in one answer, sorry for the split answer.
Thanks everyone. I think I have solved my problem and I'm pretty sure that I had four issues. For the most part I followed thatSteveguys's suggestion and read more on this article: http://haacked.com/archive/2010/04/15/sending-json-to-an-asp-net-mvc-action-method-argument.aspx
Using jQuery's post() method and specifying json as the type didn't seem to actually send it as json. By using the ajax() method and specifying json it sent it as json.
The JSON.serialize() method was also need to cleanly send over the json.
Also my ViewModel design was a big problem. We are using the MS code analytic build junk and it didn't want me having a setter for my collections in the ViewModel. So me being from a java/hibernate world, thought it didn't need them to bind and it would just come in as a serialized object magically. Once I just suppressed the error and reset up my setters. I am getting the collections now in my controller.
I believe using the MVC2 Future's Value Providers are doing something but it still doesn't convert json dates robustly, So I am still investigating the best way to do that.
I hope my issues help out others.
UPDATE: using this method to update collections of data appears to be super slow. A collection with 200 entries in it and 8 fields per entry takes 3 minutes to get to the controller. Just 1 or 2 entries take very little time. The only thing I know of that is happening between here is data binding to the model view. I don't know if MVC2 provides a easy way to send this much data and bind it.