Is there a way in Swift that I can add an element to an XML document after passing it through a function?
For example, if I pass in the text of <original> into a function, can I append it after into the XML file and save it as a new XML document?
input XML
<original>item-id-01</original>
custom function
func doThings(_ string: String) -> String { ... }
doThings("item-id-01")
output XML
<original>item-id-01</original>
<destination>output from custom function</destination>
I'm trying to parse an XML file without using a library but all the tutorials I've seen only seem to only show how to append one element into an array, and display that into the UI.
That is, most examples:
<book> --> struct Book {
<title>My book</title> --> var title: String
<author>Dr. ABC</author> --> var author: String
</book> --> }
The XMLParser and XMLParserDelegate will search for the elementName == "book" and then append that to an array of var books: [Book]. This seems to be the popular use of XML parsing since it was the way to get data before JSON was the standard.
However, I want to be able to pass in my XML file:
<?xml version="1.0" encoding="UTF-8"?>
<data version="1.2">
<file file-location="Local/Folder/file-a.txt" original="192.168.0.1" destination="192.168.0.2">
<header>
<tool id="com.apple.TextEdit" name="TextEdit" version="14.0" build="123ABC"/>
</header>
<body>
<item id="item-id-01" xml:space="preserve">
<original>item-id-01</original>
<note>Notes: no notes recorded</note>
</item>
</body>
</file>
<file file-location="Local/Folder/file-b.txt" original="192.168.10.1" destination="192.168.10.2">
<header>
<tool id="com.apple.TextEdit" name="TextEdit" version="14.0" build="123ABC"/>
</header>
<body>
<item id="item-id-02" xml:space="preserve">
<original>item-id-02</original>
<note>Notes: no notes recorded</note>
</item>
</body>
</file>
</data>
Get all the <original> elements, pass them into the doThings() function, and have an XML document that looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<data version="1.2">
<file file-location="Local/Folder/file-a.txt" original="192.168.0.1" destination="192.168.0.2">
<header>
<tool id="com.apple.TextEdit" name="TextEdit" version="14.0" build="123ABC"/>
</header>
<body>
<item id="item-id-01" xml:space="preserve">
<original>item-id-01</original>
<destination>output from custom function</destination> // <--- custom text
<note>Notes: no notes recorded</note>
</item>
</body>
</file>
<file file-location="Local/Folder/file-b.txt" original="192.168.10.1" destination="192.168.10.2">
<header>
<tool id="com.apple.TextEdit" name="TextEdit" version="14.0" build="123ABC"/>
</header>
<body>
<item id="item-id-02" xml:space="preserve">
<original>item-id-02</original>
<destination>output from custom function</destination> // <--- custom text
<note>Notes: no notes recorded</note>
</item>
</body>
</file>
</data>
When I use the XMLParserDelegate, I'm able to extract all the <original> text into an array of Item() models:
struct Item {
var original: String
var destination: String?
var note: String
}
Then compactMap the Item.original into a String array, so I can pass it to my function:
let items: [Item] = []
let original: [String] = []
init() {
items = XMLParser.getDataFrom(url: ...)
orignal = items.compactMap({ $0.original })
}
func doThings(_ string: [String]) -> [String] { ... }
let output = doThings(original)
But how would I inject into the original XML document and essentially "Save As.." into a new file?
Things I tried
adding the entire XML document into their own struct models and looping through all of them to rebuild
- this didn't work for me as I couldn't get the attributes into the model and output it as it originally was
- eg.
<file file-location="Local/Folder/file-a.txt" original="192.168.0.1" destination="192.168.0.2">into
struct File { var header: Header var body: Body var atributes: [String : String] }convert the XML to a string, find all the
</original>and replace it with</original><destination>loopValue[index]</destination>this didn't work because if there already was a
<destination>in the original XML document, then I would end up with</original><destination>loopValue[index]</destination><destination>.. original document data</destination>
Am I not using the correct API or is there a good way to achieve this outcome?