XMLCoder icon indicating copy to clipboard operation
XMLCoder copied to clipboard

UnkeyedEncodingContainer results in incorrect XML string

Open CineDev opened this issue 7 years ago • 0 comments

There's some bugs hadling ordered arrays i.e. unkeyed containers. Let's say, I have a tree with some nodes.

let tree = TreeNode()

// create sub node with a child
let subNode1 = TreeNode()
let subSubNode1 = TreeNode()
subNode1.addChild(subSubNode1)

// create another sub node with a child
let subNode2 = TreeNode()
let subSubNode2 = TreeNode()
subNode2.addChild(subSubNode2)

// add children to that child
let subSubSubNode1 = TreeNode()
let subSubSubNode2 = TreeNode()
subSubNode2(subSubSubNode1)
subSubNode2(subSubSubNode2)

// now add all of that to the tree
tree.addChild(subNode1)
tree.addChild(subNode2)

Now I'm encoding the tree as array of arrays.

func encode(to encoder: Encoder) throws {
	var nodeContainer = encoder.container(keyedBy: OutlineTree.CodingKeys.self)
	try nodeContainer.encode(element, forKey: .node)
	
	guard !children.isEmpty else { return }
	
	var childrenContainer = nodeContainer.nestedUnkeyedContainer(forKey: .children)
	try children.forEach{ try childrenContainer.encode($0) }
}

The tree is stored in some Project object which is encoded like this:

public func encode(to encoder: Encoder) throws {
	var container = encoder.container(keyedBy: CodingKeys.self)
	try container.encode(identifier, forKey: .identifier)
	var contentContainer = container.nestedUnkeyedContainer(forKey: .content)
	try content.rootTree.children.forEach{ try contentContainer.encode($0) }
}

So, here's the problem. If I use builtin JSONEncoder everything is fine. I get this string:

{
  "identifier" : "7A71AE91-D0F6-4837-9E66-3C9E4FF06702",
  "content" : [
    {
      "node" : {
        "type" : "subNode1",
      },
      "children" : [
        {
          "node" : {
            "type" : "subSubNode1",
          }
        }
      ]
    },
    {
      "node" : {
        "type" : "subNode2",
      },
      "children" : [
        {
          "node" : {
            "type" : "subSubNode2",
          },
          "children" : [
            {
              "node" : {
                "type" : "subSubSubNode1",
              }
            },
            {
              "node" : {
                "type" : "subSubSubNode2",
              }
            }
          ]
        }
      ]
    }
  ]
}

But if I'm using the XMLEncoder, I'm gettin the messed up string:

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <content>
        <node type="subNode1" />
        <children>
            <node type="subSubNode1" />
        </children>
    </content>
    <content>
        <node type="subNode2" />
        <children>
            <node type="subSubNode2" />
            <children>
                <node type="subSubSubNode1" />
            </children>
            <children>
                <node type="subSubSubNode2" />
            </children>
        </children>
    </content>
</project>

It creates a key for every item in an array which is wrong, as far as I understand. It should create a key for an unkeyed container as a whole instead of keying every item in the container with that key.

P.S.: that XML string is impossible to decode correctly.

CineDev avatar Feb 15 '19 16:02 CineDev