2013-04-17 16 views
8

Czy istnieje sposób na utworzenie konwertera lub jakiejś operacji wykonywanej po każdej konwersji? Dla kontekstu, próbuję wypełnić domyślne wartości pól, które nie są w moim XML, aby zachować kompatybilność wsteczną, jeśli mój model danych się zmieni. Na przykład, gdybym miał ten obiekt:Ustaw domyślną wartość dla pól spoza XML w XStream

class A { 
    private String b; 
    private String c; 
    private String d; 
} 

i mój XML było coś takiego:

<a> 
<b>b</b> 
<d>d</d> 
</a> 

Chcę moje importu XML, aby wiedzieć, że nie jest wartością domyślną dla pola c że to "c" i ustaw go jako A. Powinna to być operacja ogólna, do której mogę dodać wartości domyślne do dowolnego pola bardzo złożonego wykresu. Jeśli był jakiś sposób wywołania funkcji po każdej konwersji, mógł sprawdzić bieżący obiekt na mapie obiektów, dla których chciałbym ustawić domyślną wartość.

Zauważ, że używanie readResolve/readObject nie wydaje się być opcją, ponieważ 1. readObject() nigdy nie działało dla mnie w ogóle i 2. readResolve nadpisałby pole z wartością domyślną, nawet jeśli faktycznie zawarte w XML. Daj mi znać, jeśli moje założenia tutaj są złe.

Edit :: Znalazłem ten wątek powiązany z listy mailingowej użytkownika: http://article.gmane.org/gmane.comp.java.xstream.user/4619/match=default+value

i wydaje się, że tylko proponowane rozwiązanie jest użycie readResolve() których już mówiłem nie było ważne rozwiązanie.

Odpowiedz

1

Potrzebujesz konwertera.

Oto przykładowy kod dla przypadku:

public class AConverter implements Converter { 

    @Override 
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { 
    A a = new A(); 
    String bValue = "b"; 
    String cValue = "c"; 
    String dValue = "d"; 

    while (reader.hasMoreChildren()) { 
     reader.moveDown(); 
     if ("b".equals(reader.getNodeName())) { 
      bValue = reader.getValue(); 
     } else if ("c".equals(reader.getNodeName())) { 
      cValue = reader.getValue(); 
     } else if ("d".equals(reader.getNodeName())) { 
      dValue = reader.getValue(); 
     } 
     reader.moveUp(); 
    } 
    a.setB(bValue); 
    a.setC(cValue); 
    a.setD(dValue); 

    return a; 
} 

@Override 
public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) { 
    A a = (A) object; 
    writer.startNode("b"); 
    writer.setValue(a.getB()); 
    writer.endNode(); 
    writer.startNode("c"); 
    writer.setValue(a.getC()); 
    writer.endNode(); 
    writer.startNode("d"); 
    writer.setValue(a.getD()); 
    writer.endNode(); 


} 

@Override 
public boolean canConvert(Class clazz) { 
    return clazz == A.class; 
} 

}

Nie zapomnij zarejestrować konwerter:

XStream xs = new XStream(); 
xs.registerConverter(new AConverter()); 

EDIT: Fixed kod konwertera.

+0

Dziękuję pablosaraiva, ale będzie to działać wyłącznie dla niektórych klas A. Potrzebuję tego do ogólnej pracy z dowolnym obiektem na moim pełnym grafie obiektu. Musi więc działać dla obiektów B/C/D i dowolnych obiektów, które może mieć B C D. Ponadto, jeśli dodatkowe pola zostały dodane w tym przypadku, wymagałoby to utrzymania tego konwertera. Idealnie, mógłbym tylko powiedzieć coś jak newDefaultField (classWithNewDefault.class, newFieldName, newDefaultValue) za każdym razem, gdy potrzebuję dodać nową wartość domyślną. – eipark

+0

To tylko zabawkowe rozwiązanie problemu zabawek. Prawdopodobnie będziesz potrzebował konwertera dla każdej klasy z wartościami domyślnymi. Nie musisz budować obiektów ręcznie na nieagresywnych, możesz mieć fabrykę, która się tym zajmie. Mam nadzieję, że to pomoże. – pablosaraiva

+0

W prawo. Myślę, że celem jest uniknięcie prawdziwej konserwacji, która mogłaby szybko się skomplikować i wymknąć spod kontroli. Niestety nie sądzę, że XStream oferuje lepsze rozwiązanie tego poza konwerterami dla każdego lub readResolve() wszędzie. – eipark

1

c zostanie ustawiony na null, gdy kod XML nie ma wartości ustawionej na c. Możesz następnie sprawdzić w swoim programie pobierającym wartość null i zwrócić odpowiednią wartość domyślną.

7

Użyj PureJavaReflectionProvider

XStream xstream = new XStream(new PureJavaReflectionProvider()); 

i tylko zainicjować obiektu z wartości domyślnych, jak zwykle. Albo poprzez inicjalizację pola lub kod konstruktora.

tło

Jeśli nie określisz ReflectionProvider XStream próbuje znaleźć najlepszego dostawcę refleksji. Ale najlepszy ReflectionProvider dla xstream może nie być najlepszy dla ciebie, ponieważ zwykle wybiera Sun14ReflectionProvider.

Urządzenie Sun14ReflectionProvider używa tej samej strategii tworzenia wystąpień co mechanizm serializacji java, co oznacza, że ​​pomija kod konstruktora lub jest bardziej szczegółowy - kod inicjatora obiektu.

Dlatego przykład pola inicjalizacje jak

class A { 
    private String b = "DEFAULT"; 
} 

nie będzie stosowane, a także nie będą stosowane kod konstruktor np

class A { 
    private String b; 

    public A(){ 
     b = "DEFAULT"; 
    } 
} 

PureJavaReflectionProvider zamiast wykorzystania (jak sugeruje nazwa) w Java odbicie API wystąpienia obiektów, np Class.newInstance() i dlatego wykonywany jest kod inicjowania obiektu.

+0

To podejście nie działa. Nawet PureJavaReflectionProvider ustawia domyślną wartość jako null –

+0

to nie wydaje się działać ze Scala, gdzie klasa ma domyślny konstruktor wartości, tj. Klasa A (String b = "") – sowen