4

So I tried to use F# XML parsing post on the following xml (from uclassify API):

<?xml version="1.0" encoding="UTF-8" ?> 
<uclassify xmlns="http://api.uclassify.com/1/ResponseSchema" version="1.01"> 
    <status success="true" statusCode="2000"/> 
    <readCalls> 
    <classify id="cls1"> 
        <classification textCoverage="1"> 
            <class className="happy" p="0.912929"/> 
            <class className="upset" p="0.0870707"/> 
        </classification> 
    </classify> 
    </readCalls> 
</uclassify>

The code is something like:

let doc  = 
    Xdocument.Load file
doc.Element(xn "uclassify")
   .Element(xn "readCalls")
   .Element(xn "classify")
   .Element(xn "classification")
   .Element(xn "class").Attribute(xn "p")

This does not work!!! Seems it can't get the Parsing done. Yet removing the attribute xmlns="http://api.uclassify.com/1/ResponseSchema" version="1.01" makes it work:

let doc  = 
    Xdocument.Load file
    let test = IO.File.ReadAllText(file).Replace("xmlns=\"http://api.uclassify.com/1/ResponseSchema\" version=\"1.01\"","")
    XDocument.Parse(test)

doc.Element(xn "uclassify")
   .Element(xn "readCalls")
   .Element(xn "classify")
   .Element(xn "classification")
   .Element(xn "class").Attribute(xn "p")

Note this problem seems related to Why must I remove xmlns attribute .... So the question is why must I remove the xmlns attribute ? What should I use to parse xml that have an xmlns attribute ?

Thank you

Community
  • 1
  • 1
jlezard
  • 1,417
  • 2
  • 15
  • 32

2 Answers2

3

@dahlbyk's version works, but once you have an XNamespace object, you can add a string to it in F# just as you can in C#. As a result, I'd prefer this syntax as it's a bit closer to the way it's typically done in C#:

let xn = XName.Get
let xmlns = XNamespace.Get

let ns = xmlns "http://api.uclassify.com/1/ResponseSchema"

doc.Element(ns + "uclassify")
   .Element(ns + "readCalls")
   .Element(ns + "classify")
   .Element(ns + "classification")
   .Element(ns + "class")
   .Attribute(xn "p")
Joel Mueller
  • 28,324
  • 9
  • 63
  • 88
2

You need to reference elements with their namespace:

let xn (tag:string) = XName.Get(tag)
let xnuc (tag:string) = XName.Get(tag, "http://api.uclassify.com/1/ResponseSchema")

doc.Element(xnuc "uclassify")
   .Element(xnuc "readCalls")
   .Element(xnuc "classify")
   .Element(xnuc "classification")
   .Element(xnuc "class")
   .Attribute(xn "p")
dahlbyk
  • 75,175
  • 8
  • 100
  • 122
  • Don't know the linq-to-xml details, but it's possible this may be slightly faster: `let xnuc = XNamespace.Get("http://api.uclassify.com/1/ResponseSchema").GetName` – Eamon Nerbonne May 03 '11 at 13:52
  • In C# it's common to do something like `var ns = XNamespace.Get(...)` and `doc.Element(ns + "uclassify")`, but F# doesn't play nicely with the implicit casting, thus the functions to make `XName` from `string`. – dahlbyk May 03 '11 at 15:53