Converter Tutorial

本文通过实例展示了如何使用XStream进行XML序列化,包括基本的类定义、自定义转换器实现、复杂对象的处理等,并介绍了日期转换器的具体实现。

Simple Converter

Setting up a simple example

This is the most basic converter... let's start with a simple Person:

package com.thoughtworks.xstream.examples;

public class Person {

        private String name;

        public String getName() {
                return name;
        }

        public void setName(String name) {
                this.name = name;
        }

}

So let's create a person and convert it to XML...

package com.thoughtworks.xstream.examples;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

public class PersonTest {

        public static void main(String[] args) {
                Person person = new Person();
                person.setName("Guilherme");

                XStream xStream = new XStream(new DomDriver());
                System.out.println(xStream.toXML(person));
        }

}

This results in a really ugly XML code which contains the full class name (including package)...

<com.thoughtworks.xstream.examples.Person>
  <name>Guilherme</name>
</com.thoughtworks.xstream.examples.Person>

So we make use of an 'alias' to change this full class name to something more 'human', for example 'person'.

XStream xStream = new XStream(new DomDriver());
xStream.alias("person", Person.class);
System.out.println(xStream.toXML(person));

And the outcome is much easier to read (and smaller):

<person>
  <name>Guilherme</name>
</person>

Now that we have configured a simple class to play with, let's see what XStream converters can do for us...

Creating a PersonConverter

Let's create a simple converter capable of:

  1. telling its capable of converting Person's

  2. translating a Person instance in XML

  3. translate XML into a new Person

We begin creating the PersonConverter class and implementing the Converter interface:

package com.thoughtworks.xstream.examples;

import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

public class PersonConverter implements Converter {

        public boolean canConvert(Class clazz) {
                return false;
        }

        public void marshal(Object value, HierarchicalStreamWriter writer,
                        MarshallingContext context) {
        }

        public Object unmarshal(HierarchicalStreamReader reader,
                        UnmarshallingContext context) {
                return null;
        }

}

Now we tell whoever calls us that we can handle only Person's (and nothing else, including those classes which extends Person).

public boolean canConvert(Class clazz) {
        return clazz.equals(Person.class);
}

The second step is usually quite clean, unless you are dealing with generic converters.

The marshal method is responsible for translating an object to XML. It receives three arguments:

  1. the object we are trying to convert

  2. the writer were we should output the data

  3. the current marshalling context

We start casting the object to person:

Person person = (Person) value;

Now we can output the data... let's start creating a node called fullname and adding the person's name to it:

writer.startNode("fullname");
writer.setValue(person.getName());
writer.endNode();

Quite simple huh?

public void marshal(Object value, HierarchicalStreamWriter writer,
                MarshallingContext context) {
        Person person = (Person) value;
        writer.startNode("fullname");
        writer.setValue(person.getName());
        writer.endNode();
}

We could have called start/end node as many times as we would like (but remember to close everything you open)... and conversion usually takes place when calling the setValue method.

And now let's go to the unmarshal. We use the moveDown and moveUp methods to move in the tree hierarchy, so we can simply moveDown, read the value and moveUp.

Person person = new Person();
reader.moveDown();
person.setName(reader.getValue());
reader.moveUp();

Which gives us the following converter:

package com.thoughtworks.xstream.examples;

import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

public class PersonConverter implements Converter {

        public boolean canConvert(Class clazz) {
                return clazz.equals(Person.class);
        }

        public void marshal(Object value, HierarchicalStreamWriter writer,
                        MarshallingContext context) {
                Person person = (Person) value;
                writer.startNode("fullname");
                writer.setValue(person.getName());
                writer.endNode();
        }

        public Object unmarshal(HierarchicalStreamReader reader,
                        UnmarshallingContext context) {
                Person person = new Person();
                reader.moveDown();
                person.setName(reader.getValue());
                reader.moveUp();
                return person;
        }

}

Now let's register our converter and see how our application main method looks like:

package com.thoughtworks.xstream.examples;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

public class PersonTest {

        public static void main(String[] args) {
                Person person = new Person();
                person.setName("Guilherme");

                XStream xStream = new XStream(new DomDriver());
                xStream.registerConverter(new PersonConverter());
                xStream.alias("person", Person.class);
                System.out.println(xStream.toXML(person));
        }

}

Did you notice how we registered our converter? It's a simple call to registerConverter:

xStream.registerConverter(new PersonConverter());

The final result is:

<person>
  <fullname>Guilherme</fullname>
</person>

So you might say... that only changed my tree, I want to convert data!

Try using an attribute called fullname in the person tag instead of creating a new child node.

An alternative for types with String representation

Let's enhance the Person with a String representation, that contains all necessary text to recreate the instance:

package com.thoughtworks.xstream.examples;

public class Person {

        private String name;

        public String getName() {
                return name;
        }

        public void setName(String name) {
                this.name = name;
        }

        public String toString() {
                return getName();
        }
}

In this case we can simplify our Converter to

package com.thoughtworks.xstream.examples;

import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;

public class PersonConverter extends AbstractSingleValueConverter {

        public boolean canConvert(Class clazz) {
                return clazz.equals(Person.class);
        }

        public Object fromString(String str) {
                Person person = new Person();
                person.setName(string);
                return person;
        }

}

But even nicer, our XML is also simplified (using the alias for the Person class). Since the String representation is complete, a nested element is not necessary anymore:

<person>Guilherme</person>

Note, that in implementation of a SingleValueConverter is required for attributes, since these objects have to be represented by a single string only.

Date Converter

Now that we know how the Converter interface works, let's create a simple calendar converter which uses the locale to convert the information.

Our converter will receive the Locale in its constructor and we will keep a reference to it in a member variable:

package com.thoughtworks.xstream.examples;

import java.util.Locale;

import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

public class DateConverter implements Converter {

        private Locale locale;

        public DateConverter(Locale locale) {
                super();
                this.locale = locale;
        }

        public boolean canConvert(Class clazz) {
                return false;
        }

        public void marshal(Object value, HierarchicalStreamWriter writer,
                        MarshallingContext context) {
        }

        public Object unmarshal(HierarchicalStreamReader reader,
                        UnmarshallingContext context) {
                return null;
        }

}

Now let's convert anything which extends Calendar: means if instances of class clazz can be assigned to the Calendar class, they extends the abstract class Calendar:

public boolean canConvert(Class clazz) {
        return Calendar.class.isAssignableFrom(clazz);
}

Let's go for converting a Calendar in a localized string... we first cast the object to Calendar, extract its Date and then use a DateFormat factory method to get a date converter to our localized string.

public void marshal(Object value, HierarchicalStreamWriter writer,
                MarshallingContext context) {

        Calendar calendar = (Calendar) value;

        // grabs the date
        Date date = calendar.getTime();

        // grabs the formatter
        DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
                        this.locale);

        // formats and sets the value
        writer.setValue(formatter.format(date));

}

And the other way around... in order to unmarshall, we create a GregorianCalendar, retrieves the localized DateFormat instance, parses the string into a Date and puts this date in the originalGregorianCalendar:

public Object unmarshal(HierarchicalStreamReader reader,
                UnmarshallingContext context) {

        // creates the calendar
        GregorianCalendar calendar = new GregorianCalendar();

        // grabs the converter
        DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
                        this.locale);

        // parses the string and sets the time
        try {
                calendar.setTime(formatter.parse(reader.getValue()));
        } catch (ParseException e) {
                throw new ConversionException(e.getMessage(), e);
        }

        // returns the new object
        return calendar;

}

Note 1: remember that some DateFormat implementations are not thread-safe, therefore don't put your formatter as a member of your converter.

Note 2: this implementation will convert other types of Calendar's to GregorianCalendar after save/load. If this is not what you want, change your canConvert method to return true only if class equalsGregorianCalendar.

So we get the following converter:

package com.thoughtworks.xstream.examples;

import java.text.DateFormat;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;

import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

public class DateConverter implements Converter {

        private Locale locale;

        public DateConverter(Locale locale) {
                super();
                this.locale = locale;
        }

        public boolean canConvert(Class clazz) {
                return Calendar.class.isAssignableFrom(clazz);
        }

        public void marshal(Object value, HierarchicalStreamWriter writer,
                        MarshallingContext context) {
                Calendar calendar = (Calendar) value;
                Date date = calendar.getTime();
                DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
                                this.locale);
                writer.setValue(formatter.format(date));
        }

        public Object unmarshal(HierarchicalStreamReader reader,
                        UnmarshallingContext context) {
                GregorianCalendar calendar = new GregorianCalendar();
                DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
                                this.locale);
                try {
                        calendar.setTime(formatter.parse(reader.getValue()));
                } catch (ParseException e) {
                        throw new ConversionException(e.getMessage(), e);
                }
                return calendar;
        }

}

And let's try it out. We create a DateTest class with a main method:

  1. creates a calendar (current date)

  2. creates the XStream object

  3. registers the converter with a Brazilian Portuguese locale

  4. translates the object in XML

Well, we already know how to do all those steps... so let's go:

package com.thoughtworks.xstream.examples;

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

public class DateTest {

        public static void main(String[] args) {

                // grabs the current date from the virtual machine
                Calendar calendar = new GregorianCalendar();

                // creates the xstream
                XStream xStream = new XStream(new DomDriver());

                // brazilian portuguese locale
                xStream.registerConverter(new DateConverter(new Locale("pt", "br")));

                // prints the result
                System.out.println(xStream.toXML(calendar));

        }

}

The result? Well... it depends, but it will be something like:

<gregorian-calendar>Sexta-feira, 10 de Fevereiro de 2006</gregorian-calendar>

Note: we did not put any alias as gregorian-calendar is the default alias for GregorianCalendar.

And now let's try to unmarshal the result shown above:

// loads the calendar from the string
Calendar loaded = (Calendar) xStream
                .fromXML("<gregorian-calendar>Sexta-feira, 10 de Fevereiro de 2006</gregorian-calendar>");

And print it using the system locale, short date format:

// prints using the system defined locale
System.out.println(DateFormat.getDateInstance(DateFormat.SHORT).format(
                loaded.getTime()));

The result might be something like (if your system locale is American English):

2/10/06

Complex Converter

Setting up another example

We already defined some classes, so let them glue together:

package com.thoughtworks.xstream.examples;

public class Birthday {

        private Person person;
        private Calendar date;
        private char gender;

        public Person getPerson() {
                return person;
        }

        public void setPerson(Person person) {
                this.person = person;
        }

        public Calendar getDate() {
                return date;
        }

        public void setDate(Calendar date) {
                this.date = date;
        }
        
        public char getGender() {
                return gender;
        }

        public void setGenderMale() {
                this.gender = 'm';
        }

        public void setGenderFemale() {
                this.gender = 'f';
        }

}

While XStream is capable of converting this class without any problem, we write our own custom converter just for demonstration. This time we want to reuse our already written converters for the Person and the Calendar and add an own attribute for the gender. The canConvert method is plain simple. We convert no derived classes this time, since they might have additional fields. But we reuse the converters registered in XStream for our member fields and handle null values:

package com.thoughtworks.xstream.examples;

import java.util.Calendar;

import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

public class BirthdayConverter implements Converter {

        public boolean canConvert(Class clazz) {
                return Birthday.class == clazz;
        }

        public void marshal(Object value, HierarchicalStreamWriter writer,
                        MarshallingContext context) {
                Birthday birthday = (Birthday)value;
                if (birthday.getGender() != '\0') {
                        writer.addAttribute("gender", Character.toString(birthday.getGender()));
                }
                if (birthday.getPerson() != null) {
                        writer.startNode("person");
                        context.convertAnother(birthday.getPerson());
                        writer.endNode();
                }
                if (birthday.getDate() != null) {
                        writer.startNode("birth");
                        context.convertAnother(birthday.getDate());
                        writer.endNode();
                }
        }

        public Object unmarshal(HierarchicalStreamReader reader,
                        UnmarshallingContext context) {
                Birthday birthday = new Birthday();
                String gender = reader.getAttribute("gender");
                if (gender != null) {
                        if (gender.length() > 0) {              
                                if (gender.char(0) == 'f') {
                                        birthday.setGenderFemale();
                                } else if (gender.char(0) == 'm') {
                                        birthday.setFemale();
                                } else {
                                        throw new ConversionException("Invalid gender value: " + gender);
                                }
                        } else {
                                throw new ConversionException("Empty string is invalid gender value");
                        }
                }
                while (reader.hasMoreChildren()) {
                        reader.moveDown();
                        if ("person".equals(reader.getNodeName())) {
                                Person person = (Person)context.convertAnother(birthday, Person.class);
                                birthday.setPerson(person);
                        } else if ("birth".equals(reader.getNodeName())) {
                                Calendar date = (Calendar)context.convertAnother(birthday, Calendar.class);
                                birthday.setDate(date);
                        }
                        reader.moveUp();
                }
                return birthday;
        }

}

The unmarshal method ensures the valid value for the gender by throwing a ConversionException for invalid entries.

Note, that attributes will always have to be written and read first. You work on a stream and accessing the value of a tag or its members will close the surrounding tag (that is still active when the method is called).

If the implementation of Birthday ensures, that none of its fields could hold a null value and gender contains a valid value, then we could drop the null condition in the marshal method and in unmarshal we could omit the loop as well as the comparison of the tag names:

package com.thoughtworks.xstream.examples;

import java.util.Calendar;

import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

public class BirthdayConverter implements Converter {

        public boolean canConvert(Class clazz) {
                return Birthday.class == clazz;
        }

        public void marshal(Object value, HierarchicalStreamWriter writer,
                        MarshallingContext context) {
                Birthday birthday = (Birthday)value;
                writer.addAttribute("gender", Character.toString(birthday.getGender()));
                writer.startNode("person");
                context.convertAnother(birthday.getPerson());
                writer.endNode();
                writer.startNode("birth");
                context.convertAnother(birthday.getDate());
                writer.endNode();
        }

        public Object unmarshal(HierarchicalStreamReader reader,
                        UnmarshallingContext context) {
                Birthday birthday = new Birthday();
                if (reader.getAttribute("gender").charAt(0) == 'm') {
                        birthday.setGenderMale();
                } else {
                        birthday.setGenderFemale();
                }
                reader.moveDown();
                Person person = (Person)context.convertAnother(birthday, Person.class);
                birthday.setPerson(person);
                reader.moveUp();
                reader.moveDown();
                Calendar date = (Calendar)context.convertAnother(birthday, Calendar.class);
                birthday.setDate(date);
                reader.moveUp();
                return birthday;
        }

}

转载于:https://my.oschina.net/heroShane/blog/199202

### VMware Converter 使用指南及相关下载 VMware vCenter Converter 是一款功能强大的工具,主要用于将物理机、虚拟机以及其他系统迁移到 VMware 虚拟环境中[^1]。此工具不仅支持物理到虚拟 (P2V) 的迁移,还能够处理虚拟机之间的相互转换以及硬件版本的调整[^2]。 #### 官方用户指南下载 官方提供了详细的用户手册来辅助用户的操作流程。对于 VMware vCenter Converter standalone 6.2 版本,您可以访问以下链接获取最新的英文版用户指南: - **项目地址**: [https://gitcode.com/Open-source-documentation-tutorial/dd7aa](https://gitcode.com/Open-source-documentation-tutorial/dd7aa) 该文档涵盖了从基础设置到高级配置的各项细节,适合不同层次的技术人员学习和参考。 #### 迁移与管理教程 关于具体的迁移实践,《VMware vSphere Virtual Machine Management》一书中专门有一章节讨论了如何利用 VMware Converter 工具完成虚拟机的导入、迁移及硬件版本调整工作。如果您希望深入了解这些方面的内容,可以通过查阅这本书籍获得更详尽的信息。 另外,在实际应用过程中,针对特定场景也有相应的案例分析可供借鉴。例如,《VMware Converter Standalone 6.1.1 P2V迁移Linux一例》就详细描述了一个成功实施 Linux 系统由物理环境向虚拟环境过渡的过程[^3]。尽管这是稍早版本的例子,但对于理解整个迁移逻辑仍然具有很高的参考价值。 #### 实际操作中的注意事项 当使用 VMware Converter 执行 P2V 或其他形式的数据转移时,需注意保留原始系统的全部配置参数不变以便于后续兼容性验证[^4]。此外,考虑到网络连接稳定性等因素可能影响最终效果,建议提前做好充分准备并制定应急预案。 ```bash # 示例命令:启动 VMware Converter CLI 并指定任务选项 vmware-converter-cli --sourceType=physical \ --destinationType=virtualMachine \ --sourceName="PhysicalServer01" \ --destHost="esxi-host.example.com" ``` 以上脚本展示了通过命令行界面调用 VMware Converter 来创建新的虚拟实例的一个简单例子。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值