Toon posts:

[java/xml]pattern voor afhandelen XML

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik ben beezig met het inlezen van een xml bestand, maar kom nu toch wat design probleempjes tegen. Ik maak gebruik van de standaard sax parser, en dat moet ook zo blijven. Ik wil namelijk niet gebruik maken van een dom parser vanwege het mogelijk vollopen van het geheugen. Dus nu moet ik de xml elementen afhandelen. UIteraard moet elk element anders afgehandeld worden. Hiervoor heb ik apparte klasses geschreven per element.

Nu gaat het mij alleen om het aanroepen van die klasses: Hoe zorg je dat je in je Handler klasse op een nette manier dat je juiste elament klasse aanroept? ik kan natuurlijk aan de hand van de element naam de juiste klasse kiezen, maar dan wordt het een zootje met if statements, niet erg netjes dus....

Is hier een pattern voor?

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Waarom ga je niet werken met JDOM? Die kan onder de grond SAX gebruiken, en toch kan je er op de DOM manier mee praten.

En verder zou je hier eventueel een 'dispatcher' voor kunnen maken.


code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interface PersoonDispatchable{
     public void voornaam(Element e);

     public void achternaam(Element e);

     public void woonplaats(Element e);
}


PersoonDispatcher{
      public static void dispatch(Element e, PersoonDispatchable p){
           if(el.getText().equals("voornaam"){
                  p.voornaam(e);
           }else if (getText().equals("achternaam"){
                  p.achternaam(e);
           }.... etc
      }
}

[ Voor 74% gewijzigd door Alarmnummer op 25-02-2003 13:37 ]


Verwijderd

Topicstarter
Zoals ik al schreef, wil ik niet gebruik malken van een DOM implementatie. een dergelijke implementatie moet namelijk alles in het geheugen laden. Voor een klain configuratie bestandje natuurlijk uitermate geschikt, maar voor een databse van 600 MB wordt het al iets minder ;)

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Ik geloof dat die bij JDOM dus niet gebeurt. Ik weet het niet zeker. Martin (alias mbravenboer, zeurkous ;) ) is hier meer in thuis, maar zoiets staat me dus bij.

[edit]
naam aangepast.

[ Voor 20% gewijzigd door Alarmnummer op 25-02-2003 13:41 ]


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Ik heb hier een keer een aardige oplossing voor geschreven, die gebruik maakt van reflectie. In plaats van de startElement methode, werd dan een methode aangeroepen met de naam van het element: startPerson bijvoorbeeld.

Dit werktte erg goed, met name qua performance: door het cachen van Method instanties was het geheel erg snel. Het maakt je code behoorlijk schoon.

Ik denk dat er geen andere oplossing is dan reflectie of volledig uitschrijven. Ik zal even kijken of ik de code ergens kan vinden.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
JDOM heeft dit probleem in principe ook: er wordt voor elke knoop een object aangemaakt en bij zeer grote XML bestanden gaat dat absoluut niet goed. Nu is JDOM wel wat efficienter dan de W3C DOM, dus treedt het probleem iets minder snel op. Er zijn overigens nog wel efficientere implementaties die zich nog meer richten op het beperken van geheugen gebruik. Ik heb hier weleens een benchmark van gezien, maar heb geen idee waar dat was.

ter info: mbravenboer = Martin ;)

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


Verwijderd

Topicstarter
Die dispatcher is wat ik op het moment heb, en dat levert dus een grote hoeveelheid if-jes op, en het breidt niet echt makkelijk uit. Het lijkt me overigen stug dat JDOM niet alles in het geheugen laad (of gebruikt hij het onderliggende xml bestand als "geheugen"). Ik zal er is naar kijken, maar ik moet ook rekening houden met performance, dus het liefst per regel het gewenste resultaat.

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Hier heb ik ook over deze "DispatchingContentHandler" geschreven:
[rml][ java] instellingen in extern bestand[/rml]

De link daar is echter dood :( . De gebruikte techniek is exact hetzelfde als de Walkabout implementatie in dit paper:
http://www.mbravenboer.org/docs/guiding-visitors-2001.pdf

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


Verwijderd

Topicstarter
mbravenboer schreef op 25 February 2003 @ 13:36:
Ik heb hier een keer een aardige oplossing voor geschreven, die gebruik maakt van reflectie. In plaats van de startElement methode, werd dan een methode aangeroepen met de naam van het element: startPerson bijvoorbeeld.

Dit werktte erg goed, met name qua performance: door het cachen van Method instanties was het geheel erg snel. Het maakt je code behoorlijk schoon.

Ik denk dat er geen andere oplossing is dan reflectie of volledig uitschrijven. Ik zal even kijken of ik de code ergens kan vinden.
Ja daar heb ik ook al aan zitten denken, dat is opzich wel een nettere manier, dan kun je ook op een makkelijke manier het aantal xml tags vergroten. Maar de performance is dus nog in orde, nou is kijken dan

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
edit: oeps, dubbel

[ Voor 100% gewijzigd door mbravenboer op 25-02-2003 13:46 ]

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Het ip adres werkt nog wel, dus ik kan hem nog even eraf halen:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*    
    Copyright (C) 2002 Martin Bravenboer

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package org.pandoramix.xml;

import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.SAXException;

import java.lang.reflect.*;

import java.util.HashMap;
import java.util.StringTokenizer;

public class DispatchingContentHandler extends DefaultHandler
{
    private static final Class[] START_ELEMENT_PARAMETERS;
    private static final Class[] END_ELEMENT_PARAMETERS;

    static
    {
        START_ELEMENT_PARAMETERS    = new Class[]{Attributes.class};
        END_ELEMENT_PARAMETERS      = new Class[0];
    }

    private final HashMap _methods;
    private final boolean _debug;

    public DispatchingContentHandler()
    {
        this(false);
    }

    public DispatchingContentHandler(boolean debug)
    {
        super();

        _methods = new HashMap();
        _debug = debug;
    }

    protected String transformElementToMethod(String qName)
    {
        StringBuffer result = new StringBuffer(qName.length());
        StringTokenizer tokenizer = new StringTokenizer(qName, "-");
        while(tokenizer.hasMoreTokens())
        {
            String token = tokenizer.nextToken();
            result.append(Character.toUpperCase(token.charAt(0)));
            result.append(token.substring(1, token.length()));
        }

        return result.toString();
    }

    public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException
    {
        Method startElementMethod = getMethod("startElement", new Class[0]);
        invoke(startElementMethod,  new Object[0]);

        Method method = getMethod("start" + transformElementToMethod(qName), START_ELEMENT_PARAMETERS);
        invoke(method,  new Object[]{atts});
    }

    public void endElement(String namespaceURI, String localName, String qName) throws SAXException
    {
        Method endElementMethod = getMethod("endElement", new Class[0]);
        invoke(endElementMethod, new Object[0]);

        Method method = getMethod("end" + transformElementToMethod(qName), END_ELEMENT_PARAMETERS);
        invoke(method, new Object[0]);
    }

    private void invoke(Method method, Object[] arguments) throws SAXException
    {
        if(method != null)
        {
            try
            {
                method.invoke(this, arguments);
            }
            catch(InvocationTargetException exc)
            {
                System.err.println("Exception caused by method: " + method.toString());

                throw new SAXException((Exception)exc.getTargetException());
            }
            catch(Exception exc)
            {
                if(exc instanceof SAXException)
                {
                    throw (SAXException)exc;
                }       
                else
                {
                    throw new SAXException(exc);
                }
            }
        }
    }

    private Method getMethod(String methodName, Class[] parameters)
    {
        Method result;

        if(_methods.containsKey(methodName))
        {
            result = (Method) _methods.get(methodName);
        }
        else
        {
            result = getMethod(getClass(), methodName, parameters);

            if(_debug && result == null)
            {
                System.out.println("No method: " + methodName);
            }

            _methods.put(methodName, result);
        }

        return result;
    }

    public static Method getMethod(Class methodClass, String methodName, Class[] parameterTypes)
    {
        try
        {
            return methodClass.getMethod(methodName, parameterTypes);
        }
        catch(NoSuchMethodException exc)
        {
            return null;
        }
        catch(SecurityException exc)
        {
            return null;
        }
    }
}


Sorry voor de layout :+ .

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


Verwijderd

Topicstarter
mer zie bo koe, dan is reflection dus de oplossing.

Verwijderd

Topicstarter
Ja, ik ben je charmeerd van je transformElementToMethod methode, dus die mag ik vast wel jatten? (Anders schrijf ik hem uiteraard zelf)

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Bijzondere methode om gecharmeerd van te zijn ;) . Je mag alles jatten. Die GPL declaratie mag je met een korreltje zout nemen.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


Verwijderd

Misschien is een snellere methode om een standaard interface te definieren voor het afhandelen van elementen en de klassenamen voor de nieuwe elementen aan een bepaalde conventie te laten voldoen zodat je ze kan laden met een Class.forName() method.
De instanties kan je dan casten naar de algemene interface.
Om andere elementen te kunnen gebruiken hoef je ze dan alleen aan het classpath toe te voegen. Een voordeel hiervan is dat je geen (trage) reflection nodig hebt.

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Bierdopje: Een voordeel hiervan is dat je geen (trage) reflection nodig hebt.
Reflection is niet percee traag: het verkrijgen van een Method instantie duurt relatief gezien vrij lang, maar als je die eenmaal hebt gaat de invocatie nagenoeg even snel als een normale aanroep. Omdat de Methods hier gecached worden en dus voor elke element naam maar 1 keer opgezocht worden, is de performance van deze code behoorlijk goed.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


Verwijderd

Ok, reflectie werkt hier natuurlijk mooi, maar voor mij de meest logische eerste optimalisatie stap tov die if lijst is gewoon een hashtable met key=tag value=Handler, waarbij Handler dan natuurlijk een interface is met handleStart(Node) en handleEnd(Node) oid en je een heel stel implementaties kunt maken per node. Nadeel hiervan tov reflectie is natuurlijk wel dat je zelf toch nog expliciet de mapping moet doen, maar daar staat tegenover dat je deze handler klassen wel weer gemakkelijk kunt hergebruiken en de mapping dynamisch kunt aanpassen.

Verwijderd

Als je de method cached bij reflection is het volgens mij ook okay ja.

Maar die mapping kan je dus dynamisch doen als je je aan conventies voor de klasse namen houd. Als je dat niet wil kan je er ook een resource file voor gebruiken. Dan hoef je de mapping in ieder geval niet in je code te doen.

Verwijderd

Topicstarter
Ik cache inderdaad ge-invoke-de tags. Op dit moment wordt een xml bestand van 16.2 mb (400000 open tags) in 20 seconden verwerkt door een specifieke klasse (overigens doe die klasses nog helemaal niks met de data). Dit is uitgevoerd op een celeron 466Mhz met 256mb ram.

Verwijderd

Hmmm dat is nog aardig wat, en hoeveel geheugen gebruikt hij dan?

  • tomato
  • Registratie: November 1999
  • Niet online
Verwijderd schreef op 27 February 2003 @ 13:03:
Op dit moment wordt een xml bestand van 16.2 mb (400000 open tags)
:o
Ik hoop dat je hier goede redenen voor hebt (ik kan maar weinig situaties bedenken waar zoveel data in een XML bestand gerechtvaardigd is)

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Zou je die file eens online kunnen zetten? Ik ben wel benieuwd hoe groot hij wordt in ATerm formaat waar ik mee werk.

http://www.stratego-langu...i/bin/view/Stratego/ATerm

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


Verwijderd

Topicstarter
mbravenboer schreef op 28 februari 2003 @ 12:00:
Zou je die file eens online kunnen zetten? Ik ben wel benieuwd hoe groot hij wordt in ATerm formaat waar ik mee werk.

http://www.stratego-langu...i/bin/view/Stratego/ATerm
Bedoel je nu dat xml bestand? Want daar is iks bijzonders aan, das gewoon een test bestand waar eigelijk continu hetzelfde instaat...
tomato schreef op 28 February 2003 @ 11:57:
:o
Ik hoop dat je hier goede redenen voor hebt (ik kan maar weinig situaties bedenken waar zoveel data in een XML bestand gerechtvaardigd is)
(generieke)database backup en informatie voor klanten (enquetes)

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
markvleth: Bedoel je nu dat xml bestand? Want daar is iks bijzonders aan, das gewoon een test bestand waar eigelijk continu hetzelfde instaat...
Ja die bedoel ik, dat er niets zinvols in staat maakt niet uit ;) . Ik neem aan dat het data achtige XML is en veel grotere test cases zijn meer document achtige XML (boeken).

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


Verwijderd

Topicstarter
Je vind het vast niet erg dat ik hem iets heb ingekort en het test bestand van 253MB waar ik nu mee werk niet post....
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" ?>
<!--<!DOCTYPE datatypes >-->
<dbms-servers>
    
    <server name="MYSQL">
        <driver>org.gjt.mm.mysql.Driver</driver>        
        <user>tinky winky</user>
        <data-types>            
            <data-type type="TEXT">TEXT</data-type>
            <data-type type="VARCHAR">VARCHAR</data-type>
            <data-type type="INTEGER">INTEGER</data-type>           
        </data-types>
    </server>
    <server name="MSSQL">
        <driver>org.ms.whatever.Driver</driver>
        <data-types>
            <data-type type="TEXT">TEXT</data-type>
            <data-type type="VARCHAR">VARCHAR</data-type>
            <data-type type="INTEGER">INTEGER</data-type>           
        </data-types>
    </server>
</dbms-servers>

Verwijderd

Topicstarter
Verwijderd schreef op 28 February 2003 @ 11:29:
Hmmm dat is nog aardig wat, en hoeveel geheugen gebruikt hij dan?
7 MB (tenminste dat staat achter javaw.exe ;) )

[ Voor 11% gewijzigd door Verwijderd op 28-02-2003 12:52 ]

Pagina: 1