microformats/microformats2-parsing

Arrays type and rels should not contain duplicate items.

Zegnat opened this issue · 4 comments

The div element in the following example really only specifies 2 classes on itself. Even if the class attribute contains three terms. And the a element creates 2 different relations between the source document and the URL in href, even with three terms in the rel attribute.

<div class="h-entry h-cite h-entry">
  <a href="#" rel="me bookmark me"></a>
</div>

If we compare the development version of the Python parser, with the Go parser, the issue becomes clear. The Python parser only shows unique values for ["items"][0].type and ["rel-urls"]["#"].rels, while the Go parser will show duplicate h-entry and me values there.

The class and rel attributes in HTML are the only ones microformats parsing depends on that are sets in the source HTML where duplicate terms have no effect. These are mapped to arrays in type and rels respectively.

The proposed solution is to:

  1. define that only unique items should be added to type and rels.

This is actually already the case for rels:

set the value of that "rels" key to an array of all unique items in the set of rel values unioned with the current array value of the "rels" key

Parser output

Python

{
  "items": [{
      "type": [ "h-cite", "h-entry" ],
      "properties": {
        "url": [ "#" ],
        "name": [ "" ]
      }
  }],
  "rels": {
    "bookmark": [ "#" ],
    "me": [ "#" ]
  },
  "rel-urls": {
    "#": {
      "rels": [ "bookmark", "me" ],
      "text": ""
    }
  }
}

Go

{
  "items": [{
    "type": [ "h-entry", "h-cite", "h-entry" ],
    "properties": {
      "url": [ "#" ]
    }
  }],
  "rels": {
    "bookmark": [ "#" ],
    "me": [ "#", "#" ]
  },
  "rel-urls": {
    "#": {
      "rels": [ "me", "bookmark", "me" ]
    }
  }
}

also from http://pin13.net/mf2-dev/

PHP

{
    "items": [
        {
            "type": [
                "h-cite",
                "h-entry",
                "h-entry"
            ],
            "properties": {
                "name": [
                    ""
                ],
                "url": [
                    "#"
                ]
            }
        }
    ],
    "rels": {
        "me": [
            "#",
            "#"
        ],
        "bookmark": [
            "#"
        ]
    },
    "rel-urls": {
        "#": {
            "rels": [
                "me",
                "bookmark",
                "me"
            ]
        }
    }
}

Since HTML 'class' and 'rel' attributes are defined as unordered sets, we must preserve that semantic across any parsing transformations, in order to avoid introducing meaningless noise like artificial ordering which could accidentally cause a consuming application to erroneously infer and depend on such.

The parsing spec must require uniqueness in the 'type' array accordingly, and since JSON arrays do have a defined ordering (whether you want it or not), the best we can do is to define a canonical ordering that does not imply anything about the unordered source, such as alphabetical ordering of unique h-* classnames.

For 'rel' attributes, the spec http://microformats.org/wiki/microformats2-parsing#parse_a_hyperlink_element_for_rel_microformats already says the right things for treating them as sets, and notably does preserve source order in the URL sub-arrays for each rel key, which is intentional.

(Originally published at: http://tantek.com/2018/079/t2/)

  • I initially misread the logic for the rels parser, and indeed, that is already deduped! (Through unions of sets.)
  • Spec has been updated so type only contains unique names.