XML.jl icon indicating copy to clipboard operation
XML.jl copied to clipboard

Simple XML.write followed by XML.parse fails

Open TimG1964 opened this issue 10 months ago • 2 comments

I expected this to work but it doesn't. Maybe I shouldn't expect it to?

julia> using XML

julia> doc = XML.Document()
Node (depth=1) Document

julia> dec = XML.Declaration(; var"xml version"="1.0", encoding="UTF-8")
Node (depth=1) Declaration <?xml xml version="1.0" encoding="UTF-8"?>

julia> test = XML.Element("test")
Node (depth=1) Element <test>

julia> push!(doc, dec)
1-element Vector{Node}:
 Node (depth=1) Declaration <?xml xml version="1.0" encoding="UTF-8"?>

julia> push!(doc, test)
2-element Vector{Node}:
 Node (depth=1) Declaration <?xml xml version="1.0" encoding="UTF-8"?>
 Node (depth=1) Element <test>

julia> XML.nodetype(doc) == XML.Document
true

julia> doc == XML.parse(XML.Node, XML.write(doc))
ERROR: MethodError: no method matching isless(::Int64, ::Nothing)
The function `isless` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  isless(::Missing, ::Any)
   @ Base missing.jl:87
  isless(::Any, ::Missing)
   @ Base missing.jl:88
  isless(::Real, ::AbstractFloat)
   @ Base operators.jl:179
  ...

Stacktrace:
  [1] <(x::Int64, y::Nothing)
    @ Base .\operators.jl:353
  [2] >(x::Nothing, y::Int64)
    @ Base .\operators.jl:379
  [3] get_attributes(data::Vector{UInt8}, i::Int64, j::Int64)
    @ XML C:\Users\tim\.julia\packages\XML\sVTgr\src\raw.jl:120
  [4] attributes(o::XML.Raw)
    @ XML C:\Users\tim\.julia\packages\XML\sVTgr\src\raw.jl:164
  [5] getproperty(o::LazyNode, x::Symbol)
    @ XML C:\Users\tim\.julia\packages\XML\sVTgr\src\XML.jl:82
  [6] Node(node::LazyNode)
    @ XML C:\Users\tim\.julia\packages\XML\sVTgr\src\XML.jl:163
  [7] iterate(g::Base.Generator{Vector{LazyNode}, Type{Node}}, s::Int64)
    @ Base .\generator.jl:48
  [8] collect_to!(dest::Vector{Node}, itr::Base.Generator{Vector{LazyNode}, Type{Node}}, offs::Int64, st::Int64)
    @ Base .\array.jl:849
  [9] collect_to_with_first!(dest::Vector{Node}, v1::Node, itr::Base.Generator{Vector{LazyNode}, Type{Node}}, st::Int64)
    @ Base .\array.jl:827
 [10] _collect(c::Vector{LazyNode}, itr::Base.Generator{Vector{…}, Type{…}}, ::Base.EltypeUnknown, isz::Base.HasShape{1})
    @ Base .\array.jl:821
 [11] collect_similar(cont::Vector{LazyNode}, itr::Base.Generator{Vector{LazyNode}, Type{Node}})
    @ Base .\array.jl:720
 [12] map(f::Type, A::Vector{LazyNode})
    @ Base .\abstractarray.jl:3371
 [13] Node(node::LazyNode)
    @ XML C:\Users\tim\.julia\packages\XML\sVTgr\src\XML.jl:166
 [14] Node
    @ C:\Users\tim\.julia\packages\XML\sVTgr\src\XML.jl:169 [inlined]
 [15] parse
    @ C:\Users\tim\.julia\packages\XML\sVTgr\src\XML.jl:201 [inlined]
 [16] parse(::Type{Node}, str::String)
    @ XML C:\Users\tim\.julia\packages\XML\sVTgr\src\XML.jl:290
 [17] top-level scope
    @ REPL[8]:1

TimG1964 avatar Feb 14 '25 09:02 TimG1964

Sorry for leaving this untouched for so long. Parsing is pretty ad-hoc/experimental/not well tested. I have a rewrite in the works that will make it better/well tested but it'll be a few weeks before it's ready.

joshday avatar Mar 13 '25 01:03 joshday

Just ran a quick check of this in 0.3.8 and made several observations:

dec = XML.Declaration(; var"xml version"="1.0", encoding="UTF-8") is incorrect and should be dec = XML.Declaration(; version="1.0", encoding="UTF-8")

With this change, and using v0.3.8, the test that previously errored no longer does but instead returns false:

julia> doc == XML.parse(XML.Node, XML.write(doc))
false

But this test works:

julia> XML.write(doc)==XML.write(XML.parse(XML.Node, XML.write(doc)))
true

The reason for this seems to be that parse and XML.Element generate different instantiations of (empty) children, in this case children of the <test> element:

julia> doc[2].children
Node[]      # produced by XML.Element

julia> doc2=XML.parse(XML.Node, XML.write(doc))
Node Document (2 children)

julia> doc2[2].children
            # nothing - produced by parse

julia>

So I think this now boils down to another manifestation of #33 but for children instead of attributes.

TimG1964 avatar Sep 19 '25 10:09 TimG1964