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 - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Adding a thick curve to a regionplot

Suppose we have the following simple RegionPlot: f[x_] := 1 - x^2 g[x_] := 1 - 0.5 x^2 RegionPlot[{y < f[x], f[x] < y < g[x], y > g[x]}, {x, 0, 2}, {y, 0, 2}] Now I'm trying to change the curve defined by $y=g[x]$ into a thick black curve, while leaving all other boundaries in the plot unchanged. I've tried adding the region $y=g[x]$ and playing with the plotstyle, which didn't work, and I've tried BoundaryStyle, which changed all the boundaries in the plot. Now I'm kinda out of ideas... Any help would be appreciated! Answer With f[x_] := 1 - x^2 g[x_] := 1 - 0.5 x^2 You can use Epilog to add the thick line: RegionPlot[{y < f[x], f[x] < y < g[x], y > g[x]}, {x, 0, 2}, {y, 0, 2}, PlotPoints -> 50, Epilog -> (Plot[g[x], {x, 0, 2}, PlotStyle -> {Black, Thick}][[1]]), PlotStyle -> {Directive[Yellow, Opacity[0.4]], Directive[Pink, Opacity[0.4]],