Update XMLParserDelegate to use KeyValuePairs for attributes dictionary

(Eneko Alonso) #1

There are cases where Dictionary is not good enough, being the most notorious one the case where the order of keys is important and should be preserved.

A good example of this are .xib and .storyboard files, where XML nodes have attributes defined in a very specific order (defined by Xcode on write). Currently there is no way to parse the XML using XMLParser while preserving the order of the attributes.

To clarify, Xcode itself does not seem to care about the order of the attributes when loading these files, but seems to write the attributes always in a specific order. Code diffs, in the other hand, quickly become burdensome when the order of XML node attributes is not preserved.

This "pitch" is intended to update XMLParserDelegate.parser(_:didStartElement:namespaceURI:qualifiedName:attributes:) instance method to use DictionaryLiteralKeyValuePairs (or another similar data structure where the order of the keys is preserved), instead of [String : String].

There are probably other methods, or areas where DictionaryLiteralKeyValuePairs could be used instead of a Dictionary. If this pitch were to move forward, those methods should be listed in the proposal.

Thank you,

(Erik Little) #2

This is about a Foundation type right? Foundation currently doesn't have an open evolution process. You can read more about that in Introducing FoundationLite framework as a portable subset of Foundation

(Eneko Alonso) #3

Good point. Yes, seems like this would be a request for the Foundation team. What is the best way to do that?

(Alejandro Martinez) #4

Is this a type that should be used in API surface? It seemed to me that it was just a type "for the language" in order to capture what is defined with a dictionary literal. In any case it would be nice to have APIs that preserved that order (sometimes is even desirable with JSONs of some web APIs we have to deal with).

(Eneko Alonso) #5

@Alejandro_Martinez correct, DictionaryLiteral is a public type in the swift standard library and as such, it can be used by types defined in the stdlib and other frameworks, like Foundation.

It definitely has a very confusing name, as most people assume, like you, that it is related with dictionary literals in the code. But it is not, it just has a bad name. There is a pretty long thread intended to give it a better name (eg.KeyValueList or KeyValueArray): 100% bikeshed topic: DictionaryLiteral. Here is the proposal: SE-0214 - Renaming the DictionaryLiteral type to KeyValueList

(Alejandro Martinez) #6

ups! now that you link that thread I remembered reading it!

(Erik Little) #7

I believe the proper way to submit feature requests for apple things is via their bug report system. Not sure on that though.

(Eneko Alonso) #8

Thank you, I've submitted a "suggestion": https://bugreport.apple.com/web/?problemID=44188716

(Brent Royal-Gordon) #9

KeyValuePairs is like StaticString—you can't construct one at runtime—so this API could not use it. NSXMLParser could, of course, define a custom type, or simply pass an array like [(name: String, value: String)].

(Jeremy Pereira) #10

I'd be against this, XML attributes have no defined order and an application that relied on the order to interpret the meaning would be broken, but such application would start appearing if the order could be relied on.

Changing an API just because it would make diffing with a line by line comparison tool a bit harder seems wrong to me. There are alternatives like using an XML diff utility (the proper tool for the job) or just making sure you always write the attributes in a deterministic order (as Xcode does). You could, for instance, just sort the keys alphabetically before writing them.

(Eneko Alonso) #11

@jeremyp you would be surprised how much Xcode relies on maintaining a fixed order for XMLNode attributes. From Xib and Storyboards to project and workspace files.

Think about it, it would be a total nightmare if every time you saved one of those files, the attributes where written in a different order. The amount of unnecessary changes tracked by git, and the additional amount of merge conflicts would be unbearable.

While semantically the order of XML node attributes is not important, the reality is that many applications that use XML depend one way or another in supporting a predefined order for them.

(Eneko Alonso) #12

Makes sense. An array of tuples would do, for sure. Thanks!

(Jeremy Pereira) #13

If Xcode fails to read a xib file correctly when element attributes are not in a specific order, then that is a bug in Xcode. In any case, I do not believe it is true, you yourself have said:

So now we are basing the XML parsing API on another broken tool. XML files are not line oriented text files. There are many ways of formatting an XML file that leave the logical meaning of the content unchanged and that would be problematic for line oriented diffing tools.

Having said that, there is nothing in the XML spec that says an application can't write XML files with the attributes in a deterministic order e.g. alphabetically by key.

I seriously doubt that. I have dealt with many applications that read XML files and never come across one that required the attributes to be in a certain order when it reads XML files. I suspect that is at least partly due to the fact that none of the common APIs give you any idea of what order the attributes were parsed in.

(Eneko Alonso) #14

Hi @jeremyp,

Apologies for not explaining myself properly.

The order of XML attributes matters when serializing XML, whether this is for network requests or for saving to a file.

The XML standard itself requires the version attribute to be the first one on the XML definition tag.

This is valid XML:

<?xml version=“1.0” encoding=“utf-8”?>

However, the following is invalid XML:

<?xml encoding=“utf-8” version=“1.0”?>

The problem described in my post above is that, when using Foundation’s XMLParser to read XML, the order of attributes is lost because XMLParserDelegate passes them as a [String: String] dictionary.

This prevents any consuming applications from being able to serialize back that XML while maintaining attributes in the same order.

It’s not a matter of wether applications are “broken” or not, that is a personal opinion.

It is a matter of information being lost by the parser.

PS: To the Swift community: if there is a better way to submit a request to the Foundation team, please let me know. Thank you.

(Jeremy Pereira) #15

No it doesn't. It only matters when you are using non XML aware tools such as line based comparison tools.

You're talking about the XML declaration, it's not a tag. You don't even see that if you are using XMLParser.

Consuming applications are free to serialise attributes in any order they like. As I said before, they could use alphabetical order or they could have a predefined order of writing. What they are not allowed to do is assume any significance in the order when reading XML documents.

No, it is not a personal opinion. Any application that infers any meaning from the ordering of the attributes is broken.

There is no information inherent in the ordering of XML attributes. The current use of a Dictionary models the semantics of XML attributes perfectly. Using a different model that implies an ordering to the attributes would be misleading to developers.

(Ben Rimmington) #16

Can you use the XMLDocument class, instead of the XMLParser class? It's available in swift-corelibs-foundation and on macOS (but not iOS, tvOS, or watchOS).

XML Processing and Modeling

The attributes property of XMLElement is an optional array, so it might preserve the order.

(David Smith) #17

Unfortunately libxml2 (which XMLParser uses) does not guarantee attribute order either, which is indeed perfectly valid for it to do according to the spec. In practice I believe it's currently implemented to report attributes in order and namespaces not in order, but it seems unwise to try to rely on or guarantee that particular oddity.