Skip to main content

Importing malformed XML: Import[... "XMLObject"] vs ImportString[..., "XML"]


It appears that Import[ url , "XMLObject" ] is more forgiving of improperly formatted XML than ImportString[ string, "XML"]. For example, Mathematica is happy to do this


popNamesXML = 
Import["http://uscode.house.gov/popularnames/popularnames.htm",
"XMLObject"];

but complains about this


popNames = 

URLFetch["http://uscode.house.gov/popularnames/popularnames.htm"];
xml = ImportString[popNames, "XML"];

enter image description here


and barks about utf8 encoding for this


ImportString[popNames, {"HTML", "XMLObject"}]

In the Trace of the first snippet it appears these options are being passed to XMLGetString:


{"NormalizeWhitespace"->True,"IncludeNamespaces"->Automatic,
"ValidateAgainstDTD"->Automatic,"IncludeEmbeddedObjects"->None,

"AllowRemoteDTDAccess"->True,"ReadDTD"->True,"IncludeDefaultedAttributes"->False,
"AllowUnrecognizedEntities"->Auomatic,"PreserveCDATASections"->False}

But passing these to ImportString or directly to XMLGetString still results in the same errors.


This answer may be of interest but does not seem to be the problem, though I could be wrong. I tinkered with the suggestion there without luck. Does anyone know if/how I can make ImportString behave nicely here?



Answer



I am using Mathematica 8.0.4 and have no URLFetch command, so I have used Wget to download this 5915505 bytes file:


wget http://uscode.house.gov/popularnames/popularnames.htm

After downloading I evaluated



popNamesXML = Import["popularnames.htm", "XMLObject"]

And got the XMLObject without any errors. Then I Imported this file as "Text" and tried to get the XMLObject using ImportString:


popNames = Import["popularnames.htm", "Text"];
ImportString[popNames, {"HTML", "XMLObject"}]

I got a couple if the $CharacterEncoding::utf8 errors on the last step, instead of the XMLObject I got the plaintext version of the page.


So it is clear at least that the problem is not in URLFetch but in ImportString itself. I think it is worth to report it to Wolfram Support.


One workaround is to Export the popNames into a file with extension .htm, then Import it:


Export["popNames.htm", popNames, "Text"]

popNamesXML = Import["popNames.htm", "XMLObject"]

It poduces the desired XMLObject.


Another workaround is to use CharacterEncoding -> "WindowsANSI" option:


popNamesXML2 = ImportString[popNames, {"XHTML", "XMLObject"}, CharacterEncoding -> "WindowsANSI"]

It produces identical XMLObject without errors:


popNamesXML2 === popNamesXML



True

So the problem is that ImportString by default incorrectly chooses the "UTF8" encoding instead of "WindowsANSI". It looks like a bug since it is not related to global $CharacterEncoding variable: setting


$CharacterEncoding = "WindowsANSI"

does not change anything.


On the Documentation page for the "HTML" format we read:



If the character encoding of the file is not specified in the HTML file, Import uses the encoding specified by CharacterEncoding. A complete list of possible encodings is given by $CharacterEncodings.




This statement seems to be misleading for two reasons:


1) The default value for CharacterEncoding is Automatic and it is not clear how ImportString should behave in the case when character encoding is not specified in the HTML file.


2) In our case the character encoding IS specified in the HTML file on the first and fourth lines AND IS ignored when we provide explicit value for the CharacterEncoding option:





Manually deleting the "encoding='UTF-8'" and "charset=UTF-8" from the file in a plain text editor does fix incorrect behavior: now we get identical XMLObject without errors:


popNamesXML === ImportString[Import["popularnames_fixed.htm", "Text"], {"XHTML", "XMLObject"}]



True

I have also checked the server response (which should also contain the encoding information) using the -S switch of Wget:


wget -S http://uscode.house.gov/popularnames/popularnames.htm

and got



Content-Type: text/html;charset=UTF-8



It means Import gets no correct information about encoding from the server but is able to determine the correct encoding in this case.



So the final diagnosis is similar to what mfvonh suggested: Import can automatically handle the cases when incorrect encoding is specified in an HTML file while ImportString in such cases needs the correct encoding specified through the CharacterEncoding option.


Comments

Popular posts from this blog

plotting - How to draw lines between specified dots on ListPlot?

I would like to create a plot where I have unconnected dots and some connected. So far, I have figured out how to draw the dots. My code is the following: ListPlot[{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4,13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full] I have thought using ListLinePlot command, but I don't know how to specify to the command to draw only selected lines between the dots. Do have any suggestions/hints on how to do that? Thank you. Answer One possibility would be to use Epilog with Line : ListPlot[ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4, 13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full, Epilog -> { Line[ ...

dynamic - How can I make a clickable ArrayPlot that returns input?

I would like to create a dynamic ArrayPlot so that the rectangles, when clicked, provide the input. Can I use ArrayPlot for this? Or is there something else I should have to use? Answer ArrayPlot is much more than just a simple array like Grid : it represents a ranged 2D dataset, and its visualization can be finetuned by options like DataReversed and DataRange . These features make it quite complicated to reproduce the same layout and order with Grid . Here I offer AnnotatedArrayPlot which comes in handy when your dataset is more than just a flat 2D array. The dynamic interface allows highlighting individual cells and possibly interacting with them. AnnotatedArrayPlot works the same way as ArrayPlot and accepts the same options plus Enabled , HighlightCoordinates , HighlightStyle and HighlightElementFunction . data = {{Missing["HasSomeMoreData"], GrayLevel[ 1], {RGBColor[0, 1, 1], RGBColor[0, 0, 1], GrayLevel[1]}, RGBColor[0, 1, 0]}, {GrayLevel[0], GrayLevel...

Is there a way to do conditional matrix loop using 'continue'

I have the following: n = 3; m = 5; ww = RandomReal[{0, 0.1}, {n, n}]; uu = RandomReal[{0, 1}, {m, n}]; pp = RandomReal[{0, 1}, {n, n}]; ss = RandomInteger[{0, 5}, {m, n}]; Grid[{{"ww", "uu", "pp", "ss"}, {ww // TableForm, uu // TableForm, pp // TableForm, ss // TableForm}}, Spacings -> {5, 2}, Dividers -> All] where I would like to look at every element of matrix ss and produce a matrix tt , with zeroes at the locations in ss which have zeroes, and in all other positions do the following: tt = (-1/Subscript[ww, m]) Log[(1 - uu)/(Subscript[pp, m - 1])], where Subscript[ww, m] is the value at index of ww matrix and where Subscript[pp, m - 1] is the value at index-1 of pp matrix. So for example if the first value ever read from matrix ss happens to be 2, then value taken from matrix ww would be from the row 2, but from pp would be from row 1. Also how to tell difference between a 0 as a valid value from within the matrix elemen...