Avatar billede jpi Mester
08. maj 2018 - 15:46 Der er 11 kommentarer og
1 løsning

XML med fejl i

Hej

Jeg skal indlæse noget XML data, men filen indeholder noget "snavs".

F.eks.:
</EXDSMessage>
%hd&)
<?xml version="1.0" encoding="ISO-8859-1" standalone="no" ?>
<!DOCTYPE EXDSMessage SYSTEM "EXDS.dtd">

"Snavset" er ikke det samme hvert sted.
Kan man få VB.NEt til at ignorere dette "snavs" under indlæsningen eller skal man gennemgå filen og rydde op inden man indlæser?
Jeg har pt. en oprydnings-funktion, men den er temmelig langsom i forhold til tiden det tager at indlæse dataene...
Avatar billede arne_v Ekspert
08. maj 2018 - 16:01 #1
Er snavset altid i starten?

Det burde vaere rimeligt hurtigt at filtere input foer XML parsing.
Avatar billede jpi Mester
08. maj 2018 - 22:09 #2
Ja, det er altid i starten af hver root-tag
Avatar billede arne_v Ekspert
09. maj 2018 - 04:50 #3
Simpelt eksempel:


Imports System
Imports System.IO
Imports System.Xml

Namespace E
    Public Class Program
        Private Shared Sub Test1()
            Try
                Dim doc As New XmlDocument()
                doc.Load(New StreamReader("C:\Work\dirty.xml"))
                Console.WriteLine(doc.DocumentElement.Name)
            Catch ex As Exception
                Console.WriteLine("Exception as expected")
            End Try
        End Sub
        Private Shared Sub Test2()
            Try
                Dim xmlstr As String = File.ReadAllText("C:\Work\dirty.xml")
                Dim validstart As Integer = xmlstr.IndexOf("<?xml")
                Dim doc As New XmlDocument()
                doc.Load(New StringReader(xmlstr.Substring(validstart)))
                Console.WriteLine(doc.DocumentElement.Name)
            Catch ex As Exception
                Console.WriteLine("Exception unexpected")
            End Try
        End Sub
        Public Shared Sub Main(args As String())
            Test1()
            Test2()
            Console.ReadKey()
        End Sub
    End Class
End Namespace
Avatar billede jpi Mester
09. maj 2018 - 09:34 #4
Der er dog flere "root-tags" i hver fil.
Det som er vist i eksemplet i første log findes flere hundrede gange i filen.

Dit eksempel fjerner kun den første.

Jeg har selv en rutine der renser filen, men den er langsom, så er lidt spændt på om dit forslag er hurtigere end mit :)
Avatar billede arne_v Ekspert
09. maj 2018 - 16:36 #5
Hmmm.

Der kan da kun vaere et root tag (document element) i en valid XML fil.

Skal man se paa det som:

snavs
valid XML
snavs
valid XML
snavs
valid XML
snavs
valid XML
snavs
valid XML
snavs

?
Avatar billede jpi Mester
12. juni 2018 - 09:37 #6
Undskyld det sene svar :(

Det er en logfil, hvor der bliver smidt en valid "XML-klump" ind med jævne mellemrum.
Hver klump er valid med start og sluttag, men selve filen mangler så en global start/slut tag for at hele logfilen er valid.

Mellem hver valid klump XML ligger der så noget snavs.
Avatar billede arne_v Ekspert
13. juni 2018 - 04:33 #7
Der skal nok bruges en TextReader som filterer.

Der er to muligheder:

A) indsaetter start og slut tag => et valid XML dokument
B) fjerner snavs => flere valide XML dokumenter

Hvad passer best?
Avatar billede jpi Mester
13. juni 2018 - 08:18 #8
Jeg tænker det bliver nødt til at være en kombi.

Hvis man blot indsætter start og sluttag, så bliver den et validt XML-dokument med snavs i, hvilket ikke virker...
Avatar billede arne_v Ekspert
15. juni 2018 - 03:35 #9

Imports System
Imports System.IO
Imports System.Xml

Namespace E
    Public Class FilterReader
        Inherits TextReader
        Private Class FilterBuffer
            Private tr As TextReader
            Private faketag As String
            Private startmarker As String
            Private endmarker As String
            Private line As String
            Private ix As Integer
            Private active As Boolean
            Private done As Boolean
            Public Sub New(tr As TextReader, faketag As String, startmarker As String, endmarker As String)
                Me.tr = tr
                Me.faketag = faketag
                Me.startmarker = startmarker
                Me.endmarker = endmarker
                ' fake start tag
                line = "<" & faketag & ">"
                ix = 0
                active = False
                done = False
            End Sub
            Private Function Fill() As Boolean
                ' more characters in current line
                If ix < line.Length Then
                    Return True
                End If
                ' already seen EOF
                If done Then
                    Return False
                End If
                ' get a new valid line
                Do
                    line = tr.ReadLine()
                    ' if EOF do fake end tag
                    If line Is Nothing Then
                        line = "</" & faketag & ">"
                        ix = 0
                        done = True
                        Return True
                    End If
                    ' sort out garbage from valid XML
                    If active Then
                        Dim endix As Integer = line.IndexOf(endmarker)
                        If endix >= 0 Then
                            line = line.Substring(0, endix + endmarker.Length)
                            active = False
                        End If
                    Else
                        Dim startix As Integer = line.IndexOf(startmarker)
                        If startix >= 0 Then
                            line = line.Substring(startix)
                            active = True
                            Dim endix As Integer = line.IndexOf(endmarker)
                            If endix >= 0 Then
                                line = line.Substring(0, endix + endmarker.Length)
                                active = False
                            End If
                        Else
                            ' will trigger new read
                            line = Nothing
                        End If
                    End If
                Loop While line Is Nothing OrElse line.Length = 0
                ' we got a valid line
                ix = 0
                Return True
            End Function
            Public Function Read() As Integer
                If Not Fill() Then
                    Return -1
                End If
                Dim c As Integer = AscW(line(ix))
                ix += 1
                Return c
            End Function
            Public Function ReadLine() As String
                If Not Fill() Then
                    Return Nothing
                End If
                Dim s As String = line.Substring(ix)
                ix = line.Length
                Return s
            End Function
        End Class
        Private buf As FilterBuffer
        Public Sub New(real As TextReader, faketag As String, startmarker As String, endmarker As String)
            Me.buf = New FilterBuffer(real, faketag, startmarker, endmarker)
        End Sub
        Public Overrides Function Read() As Integer
            Return buf.Read()
        End Function
        Public Overrides Function ReadLine() As String
            Return buf.ReadLine()
        End Function
        Public Overrides Function ReadToEnd() As String
            Throw New Exception("ReadToEnd does not work")
        End Function
    End Class
    Public Class Program
        Public Shared Sub Main(args As String())
            Dim xr As XmlReader = New XmlTextReader(New FilterReader(New StreamReader("C:\work\dirty.xml"), "fake", "<a>", "</a>"))
            While xr.Read()
                Console.WriteLine("type={0} name={1} value={2}", xr.NodeType, xr.Name, xr.Value)
            End While
            xr.Close()
            Console.ReadKey()
        End Sub
    End Class
End Namespace



junk

<a>
<b>1</b>
<b>2</b>
</a>

junk

<a>
<b>3</b>
<b>4</b>
</a>

junk

junk <a></a>

<a></a> junk

junk <a></a> junk




type=Element name=fake value=
type=Element name=a value=
type=Element name=b value=
type=Text name= value=1
type=EndElement name=b value=
type=Element name=b value=
type=Text name= value=2
type=EndElement name=b value=
type=EndElement name=a value=
type=Element name=a value=
type=Element name=b value=
type=Text name= value=3
type=EndElement name=b value=
type=Element name=b value=
type=Text name= value=4
type=EndElement name=b value=
type=EndElement name=a value=
type=Element name=a value=
type=EndElement name=a value=
type=Element name=a value=
type=EndElement name=a value=
type=Element name=a value=
type=EndElement name=a value=
type=EndElement name=fake value=
Avatar billede arne_v Ekspert
15. juni 2018 - 03:36 #10
Men det er den slags kode jeg hader. Det er kode som meget nemt kan indeholde fejl.
Avatar billede jpi Mester
18. juni 2018 - 10:57 #11
Super, tak.

Den er 50 % hurtigere end den oprydningsfunktion jeg selv har lavet :)

mvh
jpi
Avatar billede arne_v Ekspert
18. juni 2018 - 15:53 #12
Jeg mener at FilterReader er den rigtige loesning.

Men jeg er altsaa ikke glad for koden i FilterBuffer Fill.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester