Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maintain ordering #104

Open
vjpr opened this issue Sep 8, 2021 · 6 comments
Open

Maintain ordering #104

vjpr opened this issue Sep 8, 2021 · 6 comments
Assignees
Labels
enhancement New feature or request

Comments

@vjpr
Copy link

vjpr commented Sep 8, 2021

Is your feature request related to a problem? Please describe.

In order to do xml -> json -> xml without any re-ordering, children must always be in an array, not grouped by their tag type.

<foo>
  <bar></bar>
  <baz></baz>
  <bar></bar>
</foo>

Describe the solution you'd like

Like xml-js does with {compact: false}. An option where all children are always an array.

Describe alternatives you've considered

Manipulating the DOM instead which maintains ordering.

@vjpr vjpr added the enhancement New feature or request label Sep 8, 2021
@oozcitak
Copy link
Owner

oozcitak commented Sep 9, 2021

@vjpr
Copy link
Author

vjpr commented Sep 9, 2021

Yeh, but if you have a node that has children of different element types interspersed, it groups by the element type. Which loses ordering.

@oozcitak
Copy link
Owner

oozcitak commented Sep 9, 2021

Can you create a test case to reproduce?

@vjpr
Copy link
Author

vjpr commented Sep 9, 2021

The structure of the object seems to change depending on whether there are intersperesed children of different types or not. In order to use the serialized object, I need a consistent structure.

    <?xml version="1.0"?>
    <root>
      <person age="35"/>
      <person age="30"/>
      <ele>simple element</ele>
    </root>
      {
        "root": [
          {
            "person": [
              {
                "@age": "35"
              },
              {
                "@age": "30"
              }
            ],
            "ele": [
              "simple element"
            ]
          }
        ]
      }
    <?xml version="1.0"?>
    <root>
      <person age="35"/>
      <ele>simple element</ele>
      <person age="30"/>
    </root>
      {
        "root": [
          {
            "#": [
              {
                "person": [
                  {
                    "@age": "35"
                  }
                ]
              },
              {
                "ele": [
                  "simple element"
                ]
              },
              {
                "person": [
                  {
                    "@age": "30"
                  }
                ]
              }
            ]
          }
        ]
      }

Test case:

// @ts-nocheck

import $$ from '../TestHelpers'

describe('Replicate issue', () => {

  test('#104 - Maintain ordering for interspersed children of different element type', () => {
    const xmlA = $$.t`
    <?xml version="1.0"?>
    <root>
      <person age="35"/>
      <ele>simple element</ele>
      <person age="30"/>
    </root>
    `

    const xmlB = $$.t`
    <?xml version="1.0"?>
    <root>
      <person age="35"/>
      <person age="30"/>
      <ele>simple element</ele>
    </root>
    `

    const expectedObjA = {
      'root': {
        '#': [
          {
            'person': {
              '@age': '35'
            }
          },
          {
            'ele': 'simple element'
          },
          {
            'person': {
              '@age': '30'
            }
          }
        ]
      }
    }

    const expectedObjB = {
      'root': {
        '#': [
          {
            'person': {
              '@age': '35'
            }
          },
          {
            'person': {
              '@age': '30'
            }
          },
          {
            'ele': 'simple element'
          },
        ]
      }
    }

    const verbose = true
    const group = false
    const format = 'map'

    const docA = $$.create(xmlA)
    const docB = $$.create(xmlB)
    const objA = docA.end({format, verbose, group})
    const objB = docB.end({format, verbose, group})

    console.log(JSON.stringify(toObject(objA), null, 2))
    console.log(JSON.stringify(toObject(objB), null, 2))

    expect(toObject(objA)).toEqual(expectedObjA)
    expect(toObject(objB)).toEqual(expectedObjB)

    const xmlFromObjA = $$.create(objA).end({prettyPrint: true})
    const xmlFromObjB = $$.create(objB).end({prettyPrint: true})

    expect(xmlFromObjA).toEqual(xmlA)
    expect(xmlFromObjB).toEqual(xmlB)
  })

})

const toObject = (map = new Map) => {
  if (!(map instanceof Map)) return map
  return Object.fromEntries(Array.from(map.entries(), ([k, v]) => {
    if (v instanceof Array) {
      return [k, v.map(toObject)]
    } else if (v instanceof Map) {
      return [k, toObject(v)]
    } else {
      return [k, v]
    }
  }))
}

@vjpr
Copy link
Author

vjpr commented Sep 9, 2021

So actually, it doesn't lose ordering...but the serialized object is inconsistent, making it difficult to traverse.

I think format=map actually works as expected.

I need to work with a JSON object because I want to modify things, and then diff, which is more difficult with the DOM.

I think verbose should force all children under a # - so that regardless of interspersal of children, you get the same object structure. Or could make it a separate option.

@iMrDJAi
Copy link

iMrDJAi commented Aug 6, 2022

@oozcitak Hello! Any update on this? We can't trust that object keys are going to remain in the same order, children need to be contained inside arrays.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants