XStream使用方法总结附实例代码

Posted on

XStream使用方法总结附实例代码

发布:dxy

XStream是一个Java对象和XML相互转换的工具,很好很强大。提供了所有的基础类型、数组、集合等类型直接转换的支持。因此XML常用于数据交换、对象序列化(这种序列化和Java对象的序列化技术有着本质的区别)。 XStream中的核心类就是XStream类,一般来说,熟悉这个类基本就够用了,如果你用的更多,估计是你设计有问题,否则不需要。 XStream对象相当Java对象和XML之间的转换器,转换过程是双向的。创建XSteam对象的方式很简单,只需要new XStream()即可。 Java到xml,用toXML()方法。 Xml到Java,用fromXML()方法。 在没有任何设置默认情况下,java到xml的映射,是java成员名对应xml的元素名,java类的全名对应xml根元素的名字。而实际中,往往是xml和java类都有了,要完成相互转换,必须进行别名映射。 别名配置包含三种情况: 1、类别名,用alias(String name, Class type)。 2、类成员别名,用aliasField(String alias, Class definedIn, String fieldName) 3、类成员作为属性别名,用 aliasAttribute(Class definedIn, String attributeName, String alias),单独命名没有意义,还要通过useAttributeFor(Class definedIn, String fieldName) 应用到某个类上。 别名的配置是非常重要的,但是其中有些细节问题很重要,在例子中会专门做详细说明。 另外还有不太常用的方法: addImplicitCollection(Class ownerType, String fieldName),去掉集合类型生成xml的父节点。 registerConverter(Converter converter) ,注册一个转换器。 如果你的xml很大,或者为了安全性,以流的方式传输,那么XStream也提供丰富的API, 使用起来也非常简便。目前还用不到,暂不考虑。 如果这些基本的操作还不能满足你应用的需求,XStream提供丰富的扩展点。你可以实现自己的转换器。还可以利用XStream完成更负责的功能,比如输出其他非xml格式的数据,还可以输出html,还支持XML Dom类型数据,这些应用起来稍微复杂些。当然这些不是XStream应用的重点,也不用理会,真正需要的时候再查看API和源码研究研究。 XStream的优点很多,但是也有一些小bug,比如在定义别名中的下划线“”转换为xml后会变成“_”这个符号,很变态。因此,尽量避免在别名中实用任何符号,却是需要下划线的时候,可以考虑实用连接符“-”,这个没有问题。 另外,我们的Java Bean中,常常有一些常量,在转换过程,XStream也会将这些常量转换过去,形成常量的xml节点,这显然不是想要的结果,对于常量字段,就不做转换了。 下面给出一个非常典型的而且实用的例子,作为对总结的补充: package test; import java.util.List; /// / Created by IntelliJ IDEA.
/
User: leizhimin
/ Date: 2008-5-22 21:10:13
/
Note: Please add comment here! // public class Person { private String name; private String age; private Profile profile; private List

addlist; public Person(String name, String age, Profile profile, List
addlist) { this.name = name; this.age = age; this.profile = profile; this.addlist = addlist; } public String toString() { return "Person{" + "name='" + name + '\'' + ", age='" + age + '\'' + ", profile=" + profile + ", addlist=" + addlist + '}'; } } package test; import java.sql.Date; /// / Created by IntelliJ IDEA.
/ User: leizhimin
/
Date: 2008-5-22 21:10:32
/ Note: Please add comment here! // public class Profile { private String job; private String tel; private String remark; public Profile(String job, String tel, String remark) { this.job = job; this.tel = tel; this.remark = remark; } public String toString() { return "Profile{" + "job='" + job + '\'' + ", tel='" + tel + '\'' + ", remark='" + remark + '\'' + '}'; } } package test; /// / Created by IntelliJ IDEA.
/
User: leizhimin
/ Date: 2008-5-22 21:10:22
/
Note: Please add comment here! // public class Address { private String add; private String zipcode; public Address(String add, String zipcode) { this.add = add; this.zipcode = zipcode; } public String toString() { return "Address{" + "add='" + add + '\'' + ", zipcode='" + zipcode + '\'' + '}'; } } package test; import com.thoughtworks.xstream.XStream; import java.util.List; import java.util.ArrayList; /// / Created by IntelliJ IDEA.
/ User: leizhimin
/
Date: 2008-5-22 21:10:47
/ Note: XStream学习[http://lavasoft.blog.51cto.com] // public class TestXStream { public static void main(String args[]) { test(); } public static void test() { System.out.println("----------XStream学习:http://lavasoft.blog.51cto.com----------"); //目标对象 Address address1 = new Address("郑州市经三路", "450001"); Address address2 = new Address("西安市雁塔路", "710002"); List
addList = new ArrayList
(); addList.add(address1); addList.add(address2); Profile profile = new Profile("软件工程师", "13512129933", "备注说明"); Person person = new Person("熔岩", "27", profile, addList); //转换装配 XStream xStream = new XStream(); /////////////// 设置类别名 ///////////////// xStream.alias("PERSON", test.Person.class); xStream.alias("PROFILE", test.Profile.class); xStream.alias("ADDRESS", test.Address.class); output(1, xStream, person); ////////////// 设置类成员的别名 //////////////// //设置Person类的name成员别名Name xStream.aliasField("Name", Person.class, "name"); //[注意] 设置Person类的profile成员别名PROFILE,这个别名和Profile类的别名一致, / 这样可以保持XStream对象可以从profile成员生成的xml片段直接转换为Profile成员, / 如果成员profile的别名和Profile的别名不一致,则profile成员生成的xml片段不可 / 直接转换为Profile对象,需要重新创建XStream对象,这岂不给自己找麻烦? // xStream.aliasField("PROFILE", test.Person.class, "profile"); xStream.aliasField("ADDLIST", test.Person.class, "addlist"); xStream.aliasField("Add", test.Address.class, "add"); xStream.aliasField("Job", test.Profile.class, "job"); output(2, xStream, person); //////// 设置类成员为xml一个元素上的属性 //////// xStream.useAttributeFor(Address.class, "zipcode"); ////////////// 设置属性的别名 //////////////// xStream.aliasAttribute(test.Address.class, "zipcode", "Zipcode"); output(3, xStream, person); ////////////// 将xml转为java对象 //////×///// String person_xml = "\n" + " 熔岩\n" + " 27\n" + " \n" + " 软件工程师\n" + " 13512129933\n" + " 备注说明\n" + " \n" + " \n" + "
\n" + " 郑州市经三路\n" + "
\n" + "
\n" + " 西安市雁塔路\n" + "
\n" + "
\n" + "
"; String profile_xml = " \n" + " 软件工程师\n" + " 13512129933\n" + " 备注说明\n" + " "; String address_xml = "
\n" + " 西安市雁塔路\n" + "
"; //同样实用上面的XStream对象xStream System.out.println(xStream.fromXML(person_xml).toString()); System.out.println(xStream.fromXML(profile_xml).toString()); System.out.println(xStream.fromXML(address_xml).toString()); } public static void output(int i, XStream xStream, Object obj) { String xml = xStream.toXML(obj); System.out.println(">>>第[ " + i + "]次输出\n"); System.out.println(xml + "\n"); } } ----------XStream学习:http://lavasoft.blog.51cto.com----------

第[ 1]次输出

熔岩

27

软件工程师

13512129933

备注说明

郑州市经三路

450001

西安市雁塔路

710002

第[ 2]次输出

熔岩

27

软件工程师

13512129933

备注说明

郑州市经三路

450001

西安市雁塔路

710002

第[ 3]次输出

熔岩

27

软件工程师

13512129933

备注说明

郑州市经三路

西安市雁塔路

Person{name='熔岩', age='27', profile=Profile{job='软件工程师', tel='13512129933', remark='备注说明'}, addlist=[Address{add='郑州市经三路', zipcode='450001'}, Address{add='西安市雁塔路', zipcode='710002'}]} Profile{job='软件工程师', tel='13512129933', remark='备注说明'} Address{add='西安市雁塔路', zipcode='710002'} Process finished with exit code 0 在实际中,类的属性很多,嵌套层次也很复杂,如果仅仅使用XStream原生API来硬编码设置别名等属性,显得太生硬也难以维护。完全可以考虑通过一个xml配置文件来定义所有用到的类的别名定义(包括其成员),然后,通过读取配置构建一个XStream的工厂,在用到时候直接去取,而不是让实用者组装。我目前的一个项目中,就是这么实现的,效果非常的好。 下面我给出针对上面提出的问题一个解决方案: 思想:考虑做一个过滤器,在xml转java之前,在Java转xml之后,应用这个过滤器。这个过滤器提供将xml中的“”替换为“-”,并且将xml中的不需要的节点剔除。 在过滤之前,我实现了个转换器装配,这一步通过xml来配置,并在java中获取。 代码就省略了,这一步很灵活,关键看你的应用了。 为了能过滤xml,我们需要用Dom4j递归遍历xml文档。下面一些算法代码: //递归算法:遍历配置文件,找出所有有效的xpath private static void recursiveElement(Element element) { List elements = element.elements(); validXPathList.add(element.getPath()); if (elements.size() == 0) { //没有子元素 } else { //有子元素 for (Iterator it = elements.iterator(); it.hasNext();) { //递归遍历 recursiveElement(it.next()); } } } //递归算法:遍历xml,标识无效的元素节点 private static void recursiveFixElement(Element element) { List elements = element.elements(); if (!validXPathList.contains(element.getPath())) { element.addAttribute("delete", "true"); } if (elements.size() == 0) { //没有子元素 } else { //有子元素 for (Iterator it = elements.iterator(); it.hasNext();) { Element e = it.next(); if (!validXPathList.contains(e.getPath())) { e.addAttribute("delete", "true"); } //递归遍历 recursiveFixElement(e); } } } /// / 过滤器接口方法,转换不规范字符,剔除无效节点 / / @param xmlStr 要过滤的xml / @return 符合转换器要求的xml /*/ public static String filter(String xmlStr) { Document document = null; try { document = DocumentHelper.parseText(xmlStr.replaceAll("", "_")); //递归的调用:标记要剔除的xml元素 recursiveFixElement(document.getRootElement()); List nodeList = document.selectNodes("//@delete"); for (Node node : nodeList) { node.getParent().detach(); //剔除xml元素 } } catch (DocumentException e) { System.out.println(e.getMessage()); e.printStackTrace(); } return document.asXML(); }

-

XStream

Posted on

XStream - Frequently Asked Questions

XStream

Frequently Asked Questions

  1. Compatibility
  2. Serialization
  3. XML specifics
  4. JSON specifics
  5. Comparison to other products
  6. Scalability
  7. Uses of XStream

Compatibility

Which JDK is required to use XStream?

1.3 or later.

Does XStream behave differently across different JVMs?

XStream has two modes of operation: Pure Java and Enhanced. In pure Java mode, XStream behaves in the same way across different JVMs, however its features are limited to what reflection allows, meaning it cannot serialize certain classes or fields. In enhanced mode, XStream does not have these limitations, however this mode of operation is not available to all JVMs.

Which JVMs allow XStream to operate in enhanced mode?

Currently on the Sun, Apple, HP, IBM and Blackdown 1.4 JVMs and onwards. Also for Hitachi, SAP and Diablo from 1.5 and onwards. Support for BEA JRockit starting with R25.1.0. For all other JVMs, XStream should be used in pure Java mode.

What are the advantages of using enhanced mode over pure Java mode?

Feature Pure Java Enhanced Mode Public classes Yes Yes Non public classes No Yes Static inner classes Yes Yes Non-static inner classes No Yes Anonymous inner classes No Yes With default constructor Yes Yes Without default constructor No Yes Private fields Yes Yes Final fields Yes >= JDK 1.5 Yes

Can I use XStream in an Android application?

XStream does work in Android 1.0, but has limited capabilities. XStream does not support enhanced mode for Android. Additionally Android reports itself as implementation of JDK specification 0.9, so it is not possible to write into final fields. Android also does not include the java.beans package. Therefore you cannot use the JavaBeanConverter.

Why does XStream fail on Harmony?

Since JDK 5 it is possible according the Java specification to write into final fields using reflection. This is not yet supported by Harmony and therefore the PureJavaReflectionProvider fails. We have also already investigated into enhanced mode in Harmony, but the Harmony JVM currently crashes running the unit tests. It is simply not yet ready.

Are there plans to provide enhanced mode support to other JVMs?

Yes. Let us know which JVM you would like supported.

When should I use XStream not in enhanced mode?

Running XStream in a secured environment can prevent XStream from running in enhanced mode. This is especially true when running XStream in an applet. You may also try to use the JavaBeanConverter as alternative to the ReflectionConverter running in enhanced or pure Java mode.

Which permissions does XStream need when running with an active SecurityManager?

This depends on the mode XStream is running in. Refer to the SecurityManagerTest for details.

Why does XStream 1.2 no longer read XML generated with XStream 1.1.x?

The architecture in XStream has slightly changed. Starting with XStream 1.2 the HierarchicalStreamDriver implementation is responsible to ensure that XML tags and attributes are valid names in XML, in XStream 1.1.x this responsibility was part of the ClassMapper implementations. Under some rare circumstances this will result in an unreadable XML due to the different processing order in the workflow of such problematic tag names.

You can run XStream in 1.1 compatibility mode though: XStream xstream = new XStream(new XppDriver(new XStream11XmlFriendlyReplacer())) { protected boolean useXStream11XmlFriendlyMapper() { return true; } };

XStream 1.3 ignores suddenly annotated converters (@XStreamConverter and @XStreamConverters)?

XStream treats now all annotations the same and therefore it no longer auto-detects any annotation by default. You can configure XStream to run in auto-detection mode, but be aware if the implications. As alternative you might register the deprecated AnnotationReflectionConverter, that was used for XStream pre 1.3.x, but as drawback the functionality to register a local converter with XStream.registerLocalConverter will no longer work.

XStream 1.3 suddenly has a different field order?

Yes. This was announced with the last 1.2.x release and was done to support the type inheritance of XML schemas. However, XStream is delivered with the XStream12FieldKeySorter that can be used to sort the fields according XStream 1.2.2.

Serialization

Which class types can be serialized by XStream?

In contrast to the JDK XStream is not tied to a marker interface to serialize a class. XStream ships with some specialized converters, but will use reflection by default for "unknown" classes to examine, read and write the class' data. Therefore XStream can handle quite any class, especially the ones referred as POJO (Plain Old Java Object).

However, some types of classes exist with typical characteristics, that cannot be handled - at least not out of the box:

  1. Objects that are based on threads or thread local data: Thread, Timer, ThreadLocal and so on. These classes keep different data for different threads and there's no possibility to recreate a thread in a generic way nor recreating thread specific data. There might be special use cases, but this will always involve a custom converter where threads can be recreated in a specific way tied to that use case.
  2. Class types that are based on generated classes. Such types have often names that are unique to the current process and will have no meaning in a different process. A custom converter might help to write the appropriate data into the serializing stream to be able to recreate a equivalent class at deserialization time.
  3. Types that keep and use system resources like file handles, sockets, pipes and so on. ClassLoader, FileInputStream, FileOutputStream, Socket and so on. To deserialize such a class the converter must be able to claim the appropriate resource from the system again. With the help of a custom converter this might be possible, but with the reflection converter the deserialized class might refer a system resource that is no longer valid or belongs to somebody else. Behavior is undefined then.
  4. A very special case of such allocated system resources are those classes that keep handles to system memory directly, because they are partly implemented native. It is known to be true for the Linux version of Sun's JDK that the BufferedImage references some system specific types of the JDK that themselves have member fields with such memory handles. While it is possible at first sight to serialize and deserialize a BufferedImage, the reflection converter will also duplicate the memory handle. As a result the JVM might crash easily because of freeing unallocated memory or freeing the same memory twice. It might be possible to create a custom converter, but the data structure is really complex in this area and nobody has been investigating so far to such an extent. However, do not use the reflection converter for these types! You have been warned!

How do I specify that a field should not be serialized?

Make it

transient , specify it with

XStream.omitField() or annotate it with @XStreamOmitField

How do I initialize a transient field at deserialization?

XStream uses the same mechanism as the JDK serialization. Example: class ThreadAwareComponent { private transient ThreadLocal component; // ... private Object readResolve() { component = new ThreadLocal(); return this; } }

XStream is not calling the default constructor during deserialization.

This is, in fact, the same case as above. XStream uses the same mechanism as the JDK serialization. When using the enhanced mode with the optimized reflection API, it does not invoke the default constructor. The solution is to implement the readResolve method as above: class MyExecutor { private Object readResolve() { // do what you need to do here System.out.println("After instantiating MyExecutor"); // at the end returns itself return this; } }

What do serialized collections look like?

See example for the CollectionConverter.

Note, that it is possible to configure XStream to omit the container element toys using implicit collections.

Do my classes have to implement Serializable if XStream is to serialize them?

No.

Can dynamic proxies be serialized?

Yes.

Can CGLIB proxies be serialized?

Only limitedly. A proxy generated with the CGLIB Enhancer is supported, if the proxy uses either a factory or only one callback. Then it is possible to recreate the proxy instance at unmarshalling time. Starting with XStream 1.3.1 CGLIB support is no longer automatically installed because of possible classloader problems and side-effects, because of incompatible ASM versions. You can enable CGLIB support with: XStream xstream = new XStream() { protected MapperWrapper wrapMapper(MapperWrapper next) { return new CGLIBMapper(next); } }; xstream.registerConverter(new CGLIBEnhancedConverter(xstream.getMapper(), xstream.getReflectionProvider()));

CGLIBEnhancedConverter fails at initialization with ExceptionInInitializerError

This is not a problem of XStream. You have incompatible ASM versions in your classpath. CGLIB 2.1.x and below is based on ASM 1.5.x which is incompatible to newer versions that are used by common packages like Hibernate, Groovy or Guice. Check your dependencies and ensure that you are using either using cglib-nodep-2.x.jar instead of cglib-2.x.jar or update to cglib-2.2.x that depends on ASM 3.1. However, the nodep version contains a copy of the ASM classes with private packages and will therefore not raise class incompatibilities at all.

Serialization fails with NoSuchMethodError: net.sf.cglib.proxy.Enhancer.isEnhanced(Ljava/lang/Class;)Z

XStream uses this method to detect a CGLIB-enhanced proxy. Unfortunately the method is not available in the cglib-2.0 version. Since this version is many years old and the method is available starting with cglib-2.0.1, please consider an upgrade of the dependency, it works usually smoothly.

My attributes are interpreted by XStream itself and cause unexpected behavior

XStream's generic converters and the marshalling strategies use a number of attributes on their own. Especially the attributes named id, class and reference are likely to cause such collisions. Main reason is XStream's history, because originally user defined attributes were not supported and all attribute were system generated. Starting with XStream 1.3.1 you can redefine those attributes to allow the names to be used for your own ones. The following snippet defines XStream to use different system attributes for id and class while the field id of YourClass is written into the attribute class: XStream xstream = new XStream() { xstream.useAttributeFor(YourClass.class, "id"); xstream.aliasAttribute("class", "id"); xstream.aliasSystemAttribute("type", "class"); xstream.aliasSystemAttribute("refid", "id");

Can I select the field order in which XStream serializes objects?

Yes. XStream's ReflectionConverter uses the defined field order by default. You can override it by using an specific FieldKeySorter: SortableFieldKeySorter sorter = new SortableFieldKeySorter(); sorter.registerFieldOrder(MyType.class, new String[] { "firstToSerialize", "secondToSerialize", "thirdToSerialize" }); xstream = new XStream(new Sun14ReflectionProvider(new FieldDictionary(sorter)));

How does XStream deal with newer versions of classes?

  • If a new field is added to the class, deserializing an old version will leave the field uninitialized.
  • If a field is removed from the class, deserializing an old version that contains the field will cause an exception. Leaving the field in place but declaring it as transient will avoid the exception, but XStream will not try to deserialize it.
  • If a class is renamed, aliases can be used to create an abstraction between the name used in XML and the real class name.
  • If a field is renamed, this should be treated as adding a field and removing a field.

For more advanced class migrations, you may

  • have to do custom pre-processing of the XML before sending it to XStream (for example, with XSLT or DOM manipulations)
  • declare new fields as transient
  • implement your own converter, that can handle the situation
  • add a readResolve() method to your class, that initializes the object accordingly
  • implement a custom mapper to ignore unknown fields automatically (see acceptance test CustomMapperTest.testCanBeUsedToOmitUnexpectedElements())

Future versions of XStream will include features to make these type of migrations easier.

How does XStream cope with isolated class loaders?

Serializing an object graph is never a problem, even if the classes of those objects have been loaded by a different class loader. The situation changes completely at deserialization time. In this case you must set the class loader to use with: xstream.setClassLoader(yourClassLoader);

Although XStream caches a lot of type related information to gain speed, it keeps those information in tables with weak references that should be cleaned by the garbage collector when the class loader is freed.

Note, that this call should be made quite immediately after creating the XStream and before any other configuration is done. Otherwise configuration based on special types might refer classes loaded with the wrong classloader.

XML specifics

Why does XStream not respect the encoding in the XML declaration?

XStream architecture is based on IO Readers and Writers, while the XML declaration is the responsibility of XML parsers. XStream all HierarchicalStreamDriver implementation respect the encoding since version 1.3, but only if you provide an InputStream. If XStream consumes a Reader you have to initialize the reader with the appropriate encoding yourself, since it is now the reader's task to perform the encoding and no XML parser can change the encoding of a Reader and any encoding definition in the XML header will be ignored.

Why does XStream not write an XML declaration?

XStream is designed to write XML snippets, so you can embed its output into an existing stream or string. You can write the XML declaration yourself into the Writer before using it to call XStream.toXML(writer).

Why does XStream not write XML in UTF-8?

XStream does no character encoding by itself, it relies on the configuration of the underlying XML writer. By default it uses its own PrettyPrintWriter which writes into the default encoding of the current locale. To write UTF-8 you have to provide a Writer with the appropriate encoding yourself.

Why do field names suddenly have double underscores in the generated XML?

XStream maps Java class names and field names to XML tags or attributes. Unfortunately this mapping cannot be 1:1, since some identifiers of Java contain a dollar sign which is invalid in XML names. Therefore XStream uses an XmlFriendlyReplacer to replace this character with a replacement. By default this replacement uses an underscore and therefore the replacer must escape the underscore itself also. You may provide a different configured instance of the XmlFriendlyReplacer or a complete own implementation, but you must ensure, that the resulting names are valid for XML.

XStream fails to unmarshal my given XML and I do not know why?

By default XStream is written for persistence i.e. it will read the XML it can write. If you have to transform a given XML into an object graph, you should go the other way round. Use XStream to transfer your objects into XML. If the written XML matches your schema, XStream is also able to read it. This way is much easier, since you can spot the differences in the XML much more easy than to interpret the exceptions XStream will throw if it cannot match the XML into your objects.

My parser claims the &/#x0; character to be invalid, but it was written with XStream!

Your parser is basically right! A character of value 0 is not valid as part of XML according the XML specification (see version 1.0 or 1.1), neither directly nor as character entity nor within CDATA. But not every parser respects this part of the specification (e.g. Xpp3 will ignore it and read character entities). If you expect such characters in your strings and you do not use the Xpp3 parser, you should consider to use a converter that writes the string as byte array in Base64 code. As alternative you may force the PrettyPrintWriter or derived writers to be XML 1.0 or 1.1. compliant, i.e. in this mode a StreamException is thrown.

My parser claims a control character to be invalid, but it was written with XStream!

Your parser is probably right! Control characters are only valid as part of XML 1.1. You should add an XML header declaring this version or use a parser that does not care about this part of the specification (e.g. Xpp3 parser).

Why is my element not written as XML attribute although I have configured it?

You can only write types as attributes that are represented as a single String value and are handled therefore by SingleValueConverter implementations. If your type is handled by a Converter implementation, the configuration of XStream to write an attribute (using XStream.useAttributeFor() or @XStreamAsAttribute) is simply ignored.

Why are whitespace characters missing in my attribute values after deserialization?

This is part of the XML specification and a required functionality for any XML parser called attribute value normalization. It cannot be influenced by XStream. Do not use attributes if your values contain leading or trailing whitespaces, other whitespaces than blanks, or sequences of whitespaces.

Why does XStream not have any namespace support?

Not every XML parser supports namespaces and not every XML parser that supports namespaces can be configured within XStream to use those. Basically namespaces must be supported individually for the different XML parsers and the only support for namespaces that has currently been implemented in XStream is for the StAX paser. Therefore use and configure the StaxDriver of XStream to use namespaces.

JSON specifics

Why are there two JSON driver implementations?

As always, first for historical reasons! Main difference is that the JettisonMappedXmlDriver is a thin wrapper around Jettison in combination with the StaxDriver, while the JsonHierarchicalStreamDriver uses an own more flexible implementation, but can only be used to generate JSON, deserialization is not implemented.

Why is it not possible to deserialize a JSON string starting with an array?

XStream's implementation to deserialize JSON is based on Jettison and StAX. Jettison implements a XMLStreamReader of StaX and transforms the processed JSON virtually into XML first. However, if the JSON string starts with an array it is not possible for Jettison to create a valid root element, since it has no name.

XStream fails to unmarshal my JSON string and I do not know why?

Deserialization of JSON is currently done by Jettison, that transforms the JSON string into a StAX stream. XStream itself does nothing know about the JSON format here. If your JSON string reaches some kind of complexity and you do not know how to design your Java objects and configure XStream to match those, you should have a look at the intermediate XML that is processed by XStream in the end. This might help to identify the problematic spots. Also consider then marshalling your Java objects into XML first. You can use following code to generate the XML: String json = "{\"string\": \"foo\"}"; HierarchicalStreamDriver driver = new JettisonMappedXmlDriver(); StringReader reader = new StringReader(json); HierarchicalStreamReader hsr = driver.createReader(reader); StringWriter writer = new StringWriter(); new HierarchicalStreamCopier().copy(hsr, new PrettyPrintWriter(writer)); writer.close(); System.out.println(writer.toString());

What limitations has XStream's JSON support?

JSON represents a very simple data model for easy data transfer. Especially it has no equivalent for XML attributes. Those are written with a leading "@" character, but this is not always possible without violating the syntax (e.g. for array types). Those may silently dropped (and makes it therefore difficult to implement deserialization). References are another issue in the serialized object graph, since JSON has no possibility to express such a construct. You should therefore always set the NO_REFERENCES mode of XStream.

Why are there invalid characters in my JSON representation?

The JSON spec requires any JSON string to be in UTF-8 encoding. However, XStream ensures this only if you provide an InputStream or an OutputStream. If you provide a Reader or Writer you have to ensure this requirement on your own.

The generated JSON is invalid, it contains a dash in the label!

Well, no, the JSON is valid! Please check yourself with the JSON syntax checker. However, some JavaScript libraries silently assume that the JSON labels are valid JavaScript identifiers, because JavaScript supports a convenient way to address an element, if the label is a valid JavaScript identifier: var json = {"label": "foo", "label-with-dash": "bar"}; var fooVar = json.label; // works for labels that are JavaScript identifiers var barVar = json["label-with-dash"]; // using an array index works always

As alternative you may wrap the JsonWriter and replace any dash with an underscore:

HierarchicalStreamDriver driver = new JsonHierarchicalStreamDriver() { public HierarchicalStreamWriter createWriter(Writer out) { return new WriterWrapper(super.createWriter(out)) { public void startNode(String name) { startNode(name, null); } public void startNode(String name, Class clazz) { wrapped.startNode(name.replace('-', '_'), clazz); } } } }; XStream xstream = new XStream(driver);

Comparison to other products

How does XStream compare to java.beans.XMLEncoder?

XStream is designed for serializing objects using internal fields, whereas XMLEncoder is designed for serializing JavaBeans using public API methods (typically in the form of

getXXX() ,

setXXX() ,

addXXX() and

removeXXX() methods.

How does XStream compare to JAXB (Java API for XML Binding)?

JAXB is a Java binding tool. It generates Java code from a schema and you are able to transform from those classes into XML matching the processed schema and back. Note, that you cannot use your own objects, you have to use what is generated.

Scalability

Is XStream thread safe?

Yes. Once the XStream instance has been created and configured, it may be shared across multiple threads allowing objects to be serialized/deserialized concurrently (unless you enable the auto.detection and processing of annotations). Actually the creation and initialization of XStream is quite expensive, therefore it is recommended to keep the XStream instance itself.

How much memory does XStream consume?

This cannot be answered in general, but following topics have impact on the memory:

  1. XML parser technology in use: You should use a streaming parser like Xpp3 or StAX. DOM-based parsers process the complete XML and create their document model in memory before the first converter of XStream is called.
  2. Your object model: Is it necessary to keep the complete object graph in memory at once. As alternative you might use object streams or write custom converters that can load and save objects of your object model on the fly without adding them to the object graph physically. As example see the implementation of the XmlArrayList in combination with the FileStreamStrategy to keep parts of the object graph separate.
  3. References: By default XStream supports references to the same object in an object graph. This implies that XStream keeps track of all serialized and deserialized objects internally. These references are kept with WeakReferences, so that the memory can be freed as soon as nobody references these objects anymore.
  4. XML values: Any tag and attribute value that is converted into a Java String in the object graph will use the same String instance.
  5. XStream caches: To increase performance XStream caches quite a lot like classes, converters to use, aliasing, tag names. All those caches make usage of WeakReferences or will exist only while marshalling one object graph resp. unmarshalling one input stream.

Can the performance of XStream be increased?

XStream is a generalizing library, it inspects and handles your types on the fly. Therefore it will normally be slower than a piece of optimized Java code generated out of a schema. However, it is possible to increase the performance anyway:

  • Write custom converters for those of your types that occur very often in your XML.
  • Keep a configured XStream instance for multiple usage. Creation and initialization is quite expensive compared to the overhead of XStream when calling marshall or unmarshal.
  • Use Xpp3 or StAX parsers.

Note, you should never try to optimize code for performance simply because you believe that you have detected a bottle neck. Always use proper tools like a profiler to verify where your hotspots are and whether your optimization was really successful or not.

Uses of XStream

Is XStream a data binding tool?

No. It is a serialization tool.

Can XStream generate classes from XSD?

No. For this kind of work a data binding tool such as XMLBeans is appropriate.

Why is there no SaxReader?

XStream works on a stream-based parser model, while SAX is event-based. The stream based model implies, that the caller consumes the individual tokens from the XML parser on demand, while in an event-based model the parser controls the application flow on its own and will use callbacks to support client processing. The different architecture makes it therefore impossible for XStream to use an event-driven XML parser.

Software

Evaluating XStream

Tutorials

XStream

Posted on

XStream - Annotations Tutorial

XStream

Annotations Tutorial

Motivation

Sometimes it can get tedious to call all those XStream aliases/register converter methods or you might simply like the new trend on configuring POJOs: Java annotations.

This tutorial will show you how to use some of the annotations provided by XStream in order to make configuration easier. Let's start with a custom Message class: package com.thoughtworks.xstream; package com.thoughtworks.xstream; public class RendezvousMessage { private int messageType; public RendezvousMessage(int messageType) { this.messageType = messageType; } }

Let's code the XStream calls which generate the XML file:

package com.thoughtworks.xstream; public class Tutorial { public static void main(String[] args) { XStream stream = new XStream(); RendezvousMessage msg = new RendezvousMessage(15); System.out.println(stream.toXML(msg)); } }

Results in the following XML:

15

Aliasing Annotation

The most basic annotation is the one responsible for type and field aliasing: @XStreamAlias. Let's annotate both our type and field and run the tutorial method again: @XStreamAlias("message") class RendezvousMessage { @XStreamAlias("type") private int messageType; public RendezvousMessage(int messageType) { this.messageType = messageType; } }

In some strange way, the result is the same. What happened here? XStream does not read this annotation by default as it would be impossible to deserialize the XML code. Therefore we need to tell XStream to read the annotations from this type:

public static void main(String[] args) { XStream stream = new XStream(); xstream.processAnnotations(RendezvousMessage.class); RendezvousMessage msg = new RendezvousMessage(15); System.out.println(stream.toXML(msg)); }

Note that we have called the processAnnotations method of XStream. This method registers all aliases annotations in the XStream instance passed as first argument. You may also use the overloaded version of this method taking an array of types. The resulting XML is now what we have expected:

15

If you let XStream process the annotations of a type, it will also process all annotations of the related types i.e. all super types, implemented interfaces, the class types of the members and all their generic types.

Implicit Collections

Let's add a List of content to our RendezvousMessage. We desire the same functionality obtained with implicit collections: @XStreamAlias("message") class RendezvousMessage { @XStreamAlias("type") private int messageType; private List content; public RendezvousMessage(int messageType, String ... content) { this.messageType = messageType; this.content = Arrays.asList(content); } }

public static void main(String[] args) { XStream stream = new XStream(); xstream.processAnnotations(RendezvousMessage.class); RendezvousMessage msg = new RendezvousMessage(15, "firstPart","secondPart"); System.out.println(stream.toXML(msg)); }

The resulting XML shows the collection name before its elements:

15 firstPart secondPart

This is not what we desire therefore we will annotate the content list to be recognized as an implicit collection:

@XStreamAlias("message") class RendezvousMessage { @XStreamAlias("type") private int messageType; @XStreamImplicit private List content; public RendezvousMessage(int messageType, String... content) { this.messageType = messageType; this.content = Arrays.asList(content); } }

Resulting in an XML which ignores the field name (content) of the list:

15 firstPart secondPart

We are almost there... we still want to remove the 'a' tag, and define each content part with the tag 'part'. In order to do so, let's add another attribute to our implicit collection annotation. The attribute field defines the name of the tag used for data contained inside this collection:

@XStreamAlias("message") class RendezvousMessage { @XStreamAlias("type") private int messageType; @XStreamImplicit(itemFieldName="part") private List content; public RendezvousMessage(int messageType, String... content) { this.messageType = messageType; this.content = Arrays.asList(content); } }

Resulting in a cleaner XML:

15 firstPart secondPart

Local Converters

Let's create another attribute which defines the timestamp when the message was created: @XStreamAlias("message") class RendezvousMessage { @XStreamAlias("type") private int messageType; @XStreamImplicit(itemFieldName="part") private List content; private Calendar created = new GregorianCalendar(); public RendezvousMessage(int messageType, String... content) { this.messageType = messageType; this.content = Arrays.asList(content); } }

Resulting in the following xml:

15 firstPart secondPart America/Sao_Paulo

Now we face the following problem: We want to use a custom converter locally for this Calendar, but only for this Calendar, this exact field in this exact type. Easy... let's annotate it with the custom converter annotation:

@XStreamAlias("message") class RendezvousMessage { @XStreamAlias("type") private int messageType; @XStreamImplicit(itemFieldName="part") private List content; @XStreamConverter(SingleValueCalendarConverter.class) private Calendar created = new GregorianCalendar(); public RendezvousMessage(int messageType, String... content) { this.messageType = messageType; this.content = Arrays.asList(content); } }

Let's create the custom converter:

public class SingleValueCalendarConverter implements Converter { public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { Calendar calendar = (Calendar) source; writer.setValue(String.valueOf(calendar.getTime().getTime())); } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { GregorianCalendar calendar = new GregorianCalendar(); calendar.setTime(new Date(Long.parseLong(reader.getValue()))); return calendar; } public boolean canConvert(Class type) { return type.equals(GregorianCalendar.class); } }

And we end up with the converter being used and generating the following XML:

15 firstPart secondPart 1154097812245

Attributes

The client may asks for the type tag to be an attribute inside the message tag, as follows:

firstPart secondPart 1154097812245

All you need to do is add the @XStreamAsAttribute annotation:

@XStreamAlias("message") class RendezvousMessage { @XStreamAlias("type") @XStreamAsAttribute private int messageType; @XStreamImplicit(itemFieldName="part") private List content; @XStreamConverter(SingleValueCalendarConverter.class) private Calendar created = new GregorianCalendar(); public RendezvousMessage(int messageType, String... content) { this.messageType = messageType; this.content = Arrays.asList(content); } }

Omitting Fields

Sometimes a class may contain elements that should not be part of the resulting XML. In our case we may now drop the 'messageType', since we are only interested at the content. This is easy using the @XStreamOmitField annotation: @XStreamAlias("message") class RendezvousMessage { @XStreamOmitField private int messageType; @XStreamImplicit(itemFieldName="part") private List content; @XStreamConverter(SingleValueCalendarConverter.class) private Calendar created = new GregorianCalendar(); public RendezvousMessage(int messageType, String... content) { this.messageType = messageType; this.content = Arrays.asList(content); } }

The resulting XML does not contain the type of the message anymore:

firstPart secondPart 1154097812245

Auto-detect Annotations

Until now we have always told you, that you have to call processAnnotation to configure the XStream instance with the present annotations in the different classes. However, this is only half the truth. You can run XStream also in a lazy mode, where it auto-detects the annotations while processing the object graph and configure the XStream instance on-the-fly: package com.thoughtworks.xstream; public class Tutorial { public static void main(String[] args) { XStream stream = new XStream(); xstream.autodetectAnnotations(true); RendezvousMessage msg = new RendezvousMessage(15); System.out.println(stream.toXML(msg)); } }

The resulting XML will look as expected! Nevertheless you have to understand the implications, therefore some words of warning:

  1. Chicken-and-egg problem

An XStream instance caches all class types processed for annotations. Every time XStream converts an object it will in auto-detection mode first process the object's type and all the types related (as explained above). Therefore it is no problem to serialize an object graph into XML, since XStream will know of all types in advance. This is no longer true at deserialization time. XStream has to know the alias to turn it into the proper type, but it can find the annotation for the alias only if it has processed the type in advance. Therefore deserialization will fail if the type has not already been processed either by having called XStream's processAnnotations method or by already having serialized this type. However, @XStreamAlias is the only annotation that may fail in this case.

  1. Concurrency

XStream is not thread-safe while it is configured, thread-safety is only guaranteed during marshalling and unmarshalling. However an annotation is defining a change in configuration that is now applied while object marshalling is processed. Therefore will the auto-detection mode turn XStream's marshalling process in a thread-unsafe operation any you may run under certain circumstances into concurrency problems.

  1. Exceptions

XStream uses a well-defined exception hierarchy. Normally an InitializationException is only thrown while XStream is configured. If annotations are processed on the fly they can be thrown obviously also in a marshalling process.

  1. Performance

In auto-detection mode XStream will have to examine any unknown class type for annotations. This will slow down the marshalling process until all processed types have been examined once.

Please note, that any call to XStream.processAnnotations will turn off the auto-detection mode.

Summing up

The XStream annotations support might help you configuring your class mappings in some ways, as the custom configuration will appear in your types, but might not be the solution for other problems, i.e. when you need to map the same type to two different XML 'standards'. Others might claim that the configuration should be clearly stated in a Java class and not mixed with your model, its up to you to pick the best approach in your case: Annotations or direct method calls to the XStream instance. Annotations do not provide more functionality, but may improve convenience.

Software

Evaluating XStream

Tutorials

Java对XML节点的修改、添加、删除 –By Xstream框架

Posted on

Java对XML节点的修改、添加、删除 –By Xstream框架

用最简洁的页面描述企业应用与Java艺术! 关于作者

Java对XML节点的修改、添加、删除 –By Xstream框架

12 三月, 2010 (10:36) | 代码 | 繁体 English 作者: H.E.

在J2EE、Java项目中对xml操作是一项非常常见的事情,在我认识了XStream以后,才彻底明白XML模型对象的概念,使用XStream让我XML的设计不由自主更符合OO的风格。另外,除了在设计上得到的体验,还在实际操作中得到了便捷的体验。

简单来说,我在IBM的Java开发园地上看过一些操作XML经典的文章被广为流传,但是无论是采用DOM4J还是JDOM对XML文件中的节点或者整个文件的进行 修改、添加、删除 都没有XStream 简单、方便。

下面来看一下 XStream 官方网站上的例子,网站地址: http://xstream.codehaus.org/tutorial.html。

官方网站上的例子只是提供了一个例子,并没有说明如何对 XML节点进行修改、添加、删除,XStream 官方网站上只是给出了一个从对象到XML,从XML到对象的例子。

Javabloger的作者H.E.通过实践发现采用 XStream可以很简单的对XML节点进行 修改、添加、删除,比传统的XML框架简单很多倍,用过一次的人都不会忘记,因为实在是太方便了

修改XML节点代码示例如下:

public void upDateMySQLRecentHost(String  HostID)  throws  Exception {
    List <RecentHost> list=null;
    try {
            list=getMySQLRecentHost();   **// 1. 先通过 XStream读取XML文件,变成List集合里面包含节点对象。**

            for (int i=0;i<list.size();i++){      **  //  2. 遍历整个 List 对象集合**
                if (list.get(i).getId().equals(HostID )){        **// 3. 比较 需要进行修改的节点 ID**
                    list.remove(i);                                     **//  4. 移除 指定的节点  **
                    list.add(recentHost);                          **  //  5. 添加 定义的新节点**
                }
            }                                                                **   //  完成操作,对节点进行 匹配的删除和添加 这个就修改。**

       *    //  以下代码H.E.就不一一介绍了,大致意思就是 将修改过的List 对象集合,重新写入XML中。*
            xstream.alias("RecentHost",  RecentHost.class);         
            String xml="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+xstream.toXML(list);
             System.out.println ( xml );
            FileWriter resultFile = new FileWriter(recentLoginHostCaseXMLFile);
            PrintWriter myFile = new PrintWriter(resultFile);
            myFile.println(xml);
            resultFile.close();
        }
    catch ( Exception e) {
        logger.error(e);
    }
}

添加XML节点代码示例如下:

public void AddMySQLRecentHost(RecentHost RecentHostInfo)  throws  Exception {
    List <RecentHost> list=null;
    try {
            list=getMySQLRecentHost(); **  // 1. 先通过 XStream读取XML文件,变成List集合里面包含节点对象。**
            list.add( RecentHostInfo  );    **//   2. 添加到  List集合里面。**

            xstream.alias("RecentHost",  RecentHost.class);  **// 3.进行XML对象映射**
            String xml="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+xstream.toXML(list);

         *   //  以下代码H.E.就不一一介绍了,大致意思就是 将添加过的List 对象集合,重新写入XML中。*
            FileWriter resultFile = new FileWriter(recentLoginHostCaseXMLFile);
            PrintWriter myFile = new PrintWriter(resultFile);
            myFile.println(xml);
            resultFile.close();      
        }
    catch ( Exception e) {
        logger.error(e);
    }
}

删除XML节点代码示例如下:

public void deleteMySQLRecentHost(String id)  throws  Exception {
    List <RecentHost> list=null;
    try {
            list=getMySQLRecentHost();   **// 1. 先通过 XStream读取XML文件,变成List集合里面包含节点对象。**

            for (int i=0;i<list.size();i++){      **  //  2. 遍历整个 List 对象集合**
                if (list.get(i).getId().equals(HostID )){       **// 3. 比较 需要进行修改的节点 ID**
                    list.remove(i);                                    **  //  4. 移除 指定的节点  **
                }                                                                                                                     
            }                                                             **      //  完成删除操作。**                            

*            //  以下代码H.E.就不一一介绍了,大致意思就是 将修改过的List 对象集合,重新写入XML中。*
            xstream.alias("RecentHost",  RecentHost.class);                                     
            String xml="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+xstream.toXML(list);
             System.out.println ( xml );                                                   
            FileWriter resultFile = new FileWriter(recentLoginHostCaseXMLFile);
            PrintWriter myFile = new PrintWriter(resultFile);                                      
            myFile.println(xml);                                                                                        
            resultFile.close();                                                                                           
        }                                                                                                                              
    catch ( Exception e) {                                                                                              
        logger.error(e);                                                                                                    
    }                                                                                                                                  
}

–end–

H.E.@Info: J2EE Architect/JEvangelist Life@NanJing Hobby@Photography Email@njthnet(at)gmail.com

JAVA中String.format的用法

Posted on

JAVA中String.format的用法 - xytop - 博客园

xytop

JAVA String.format 方法使用介绍

1.对整数进行格式化:%[index$][标识][最小宽度]转换方式 我们可以看到,格式化字符串由4部分组成,其中%[index$]的含义我们上面已经讲过,[最小宽度]的含义也很好理解,就是最终该整数转化的字符串最少包含多少位数字。我们来看看剩下2个部分的含义吧: 标识: '-' 在最小宽度内左对齐,不可以与“用0填充”同时使用 '/#' 只适用于8进制和16进制,8进制时在结果前面增加一个0,16进制时在结果前面增加0x '+' 结果总是包括一个符号(一般情况下只适用于10进制,若对象为BigInteger才可以用于8进制和16进制) ' ' 正值前加空格,负值前加负号(一般情况下只适用于10进制,若对象为BigInteger才可以用于8进制和16进制) '0' 结果将用零来填充 ',' 只适用于10进制,每3位数字之间用“,”分隔 '(' 若参数是负数,则结果中不添加负号而是用圆括号把数字括起来(同‘+’具有同样的限制) 转换方式: d-十进制 o-八进制 x或X-十六进制

    上面的说明过于枯燥,我们来看几个具体的例子。需要特别注意的一点是:大部分标识字符可以同时使用。

System.out.println(String.format("%1$,09d", -3123)); System.out.println(String.format("%1$9d", -31)); System.out.println(String.format("%1$-9d", -31)); System.out.println(String.format("%1$(9d", -31)); System.out.println(String.format("%1$/#9x", 5689)); //结果为: //-0003,123 // -31 //-31
// (31) // 0x1639

2.对浮点数进行格式化:%[index$][标识][最少宽度][.精度]转换方式 我们可以看到,浮点数的转换多了一个“精度”选项,可以控制小数点后面的位数。

标识: '-' 在最小宽度内左对齐,不可以与“用0填充”同时使用 '+' 结果总是包括一个符号 ' ' 正值前加空格,负值前加负号 '0' 结果将用零来填充 ',' 每3位数字之间用“,”分隔(只适用于fgG的转换) '(' 若参数是负数,则结果中不添加负号而是用圆括号把数字括起来(只适用于eEfgG的转换) 转换方式: 'e', 'E' -- 结果被格式化为用计算机科学记数法表示的十进制数 'f' -- 结果被格式化为十进制普通表示方式 'g', 'G' -- 根据具体情况,自动选择用普通表示方式还是科学计数法方式 'a', 'A' -- 结果被格式化为带有效位数和指数的十六进制浮点数

3.对字符进行格式化: 对字符进行格式化是非常简单的,c表示字符,标识中'-'表示左对齐,其他就没什么了。 4.对百分比符号进行格式化: 看了上面的说明,大家会发现百分比符号“%”是特殊格式的一个前缀。那么我们要输入一个百分比符号该怎么办呢?肯定是需要转义字符的,但是要注意的是,在这里转义字符不是“\”,而是“%”。换句话说,下面这条语句可以输出一个“12%”: System.out.println(String.format("%1$d%%", 12)); 5.取得平台独立的行分隔符: System.getProperty("line.separator")可以取得平台独立的行分隔符,但是用在format中间未免显得过于烦琐了。于是format函数自带了一个平台独立的行分隔符那就是String.format("%n")。 6.对日期类型进行格式化: 以下日期和时间转换的后缀字符是为 't' 和 'T' 转换定义的。这些类型相似于但不完全等同于那些由 GNU date 和 POSIX strftime(3c) 定义的类型。提供其他转换类型是为了访问特定于 Java 的功能(如将 'L' 用作秒中的毫秒)。 以下转换字符用来格式化时间: 'H' 24 小时制的小时,被格式化为必要时带前导零的两位数,即 00 - 23。 'I' 12 小时制的小时,被格式化为必要时带前导零的两位数,即 01 - 12。 'k' 24 小时制的小时,即 0 - 23。 'l' 12 小时制的小时,即 1 - 12。 'M' 小时中的分钟,被格式化为必要时带前导零的两位数,即 00 - 59。 'S' 分钟中的秒,被格式化为必要时带前导零的两位数,即 00 - 60 ("60" 是支持闰秒所需的一个特殊值)。 'L' 秒中的毫秒,被格式化为必要时带前导零的三位数,即 000 - 999。 'N' 秒中的毫微秒,被格式化为必要时带前导零的九位数,即 000000000 - 999999999。 'p' 特定于语言环境的 上午或下午 标记以小写形式表示,例如 "am" 或 "pm"。使用转换前缀 'T' 可以强行将此输出转换为大写形式。 'z' 相对于 GMT 的 RFC 822 格式的数字时区偏移量,例如 -0800。 'Z' 表示时区缩写形式的字符串。Formatter 的语言环境将取代参数的语言环境(如果有)。 's' 自协调世界时 (UTC) 1970 年 1 月 1 日 00:00:00 至现在所经过的秒数,即 Long.MIN_VALUE/1000 与 Long.MAX_VALUE/1000 之间的差值。 'Q' 自协调世界时 (UTC) 1970 年 1 月 1 日 00:00:00 至现在所经过的毫秒数,即 Long.MIN_VALUE 与 Long.MAX_VALUE 之间的差值。 以下转换字符用来格式化日期: 'B' 特定于语言环境的月份全称,例如 "January" 和 "February"。 'b' 特定于语言环境的月份简称,例如 "Jan" 和 "Feb"。 'h' 与 'b' 相同。 'A' 特定于语言环境的星期几全称,例如 "Sunday" 和 "Monday" 'a' 特定于语言环境的星期几简称,例如 "Sun" 和 "Mon" 'C' 除以 100 的四位数表示的年份,被格式化为必要时带前导零的两位数,即 00 - 99 'Y' 年份,被格式化为必要时带前导零的四位数(至少),例如,0092 等于格里高利历的 92 CE。 'y' 年份的最后两位数,被格式化为必要时带前导零的两位数,即 00 - 99。 'j' 一年中的天数,被格式化为必要时带前导零的三位数,例如,对于格里高利历是 001 - 366。 'm' 月份,被格式化为必要时带前导零的两位数,即 01 - 13。 'd' 一个月中的天数,被格式化为必要时带前导零两位数,即 01 - 31 'e' 一个月中的天数,被格式化为两位数,即 1 - 31。 以下转换字符用于格式化常见的日期/时间组合。 'R' 24 小时制的时间,被格式化为 "%tH:%tM" 'T' 24 小时制的时间,被格式化为 "%tH:%tM:%tS"。 'r' 12 小时制的时间,被格式化为 "%tI:%tM:%tS %Tp"。上午或下午标记 ('%Tp') 的位置可能与语言环境有关。 'D' 日期,被格式化为 "%tm/%td/%ty"。 'F' ISO 8601 格式的完整日期,被格式化为 "%tY-%tm-%td"。 'c' 日期和时间,被格式化为 "%ta %tb %td %tT %tZ %tY",例如 "Sun Jul 20 16:17:00 EDT 1969"。