/* * XmlSerializer.cs * Copyright (c) 2009 kbinani * * This file is part of bocoree. * * bocoree is free software; you can redistribute it and/or * modify it under the terms of the BSD License. * * bocoree 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. */ #if JAVA package org.kbinani.xml; import java.io.*; import java.util.*; import java.lang.reflect.*; import javax.xml.parsers.*; import javax.xml.transform.*; import javax.xml.transform.dom.*; import javax.xml.transform.stream.*; import org.w3c.dom.*; /** * .NETのSystem.Xml.Serialization.XmlSerializerと同じ書式で入出力するためのXMLシリアライザ.
* シリアライズしたいクラスには,以下のメソッドを実装しておく必要があります.
*
*
getXmlElementNameメソッド
*
フィールド名やgetter/setter名からXMLのノード名を調べる
*
isXmlIgnoredメソッド
*
アイテムをXMLに出力するかどうかを決める
*
getGenericTypeNameメソッド
*
アイテムが総称型を含むクラスの場合に,その総称型名を調べる
*
*
 *  public class Test2{
        public float value = 1.0f;
        private boolean m_b = true;
        private int m_i = 2;
        public Vector list = new Vector();
        
        public boolean isHoge(){
            return m_b;
        }
        
        public void setHoge( boolean value ){
            m_b = value;
        }
        
        public int getInteger(){
            return m_i;
        }
        
        public void setInteger( int value ){
            m_i = value;
        }
        
        public static String getXmlElementName( String name ){
            if( name.equals( "value" ) ){
                return "Value";
            }else if( name.equals( "list" ) ){
                return "List";
            }
            return name;
        }

        public static boolean isXmlIgnored( String name ){
            if( name.equals( "Integer" ) ){
                return true;
            }
            return false;
        }

        public static String getGenericTypeName( String name ){
            if( name.equals( "list" ) ){
                return "java.lang.Integer";
            }
            return "";
        }
    }
 * 
* このように実装しておくと,だいたい以下のようなXMLの入出力が可能になります. *
    <Test2>
        <Value>1.0</Value>
        <Hoge>true</Hoge>
        <List>
            <int>1</int>
            <int>2</int>
        </List>
    </Test2>
 * 
*/ public class XmlSerializer{ private Document m_document; private Class m_class; private boolean m_static_mode = false; private boolean m_indent = true; private int m_indent_width = 2; public boolean isIndent(){ return m_indent; } public void setIndent( boolean value ){ m_indent = value; } public int getIndentWidth(){ return m_indent_width; } public void setIndentWidth( int value ){ if( value < 0 ){ m_indent_width = 0; }else{ m_indent_width = value; } } public XmlSerializer( Class cls ){ m_class = cls; } public Object deserialize( InputStream stream ){ try{ DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = fact.newDocumentBuilder(); Document doc = builder.parse( stream ); Object ret = parseNode( m_class, null, doc.getDocumentElement() ); return ret; }catch( Exception ex ){ System.out.println( "XmlSerializer.deserialize; ex=" + ex ); return null; } } private Object parseNode( Class t, Class parent_class, Node node ){ NodeList childs = node.getChildNodes(); int numChild = childs.getLength(); Object obj; String str = node.getTextContent() + ""; if( t.equals( Integer.TYPE ) || t.equals( Integer.class ) ){ return Integer.parseInt( str ); }else if( t.equals( Byte.TYPE ) || t.equals( Byte.class ) ){ return Byte.parseByte( str ); }else if( t.equals( Short.TYPE ) || t.equals( Short.class ) ){ return Short.parseShort( str ); }else if( t.equals( Float.TYPE ) || t.equals( Float.class ) ){ return Float.parseFloat( str ); }else if( t.equals( Double.TYPE ) || t.equals( Double.class ) ){ return Double.parseDouble( str ); }else if( t.equals( Boolean.TYPE ) || t.equals( Boolean.class ) ){ return Boolean.parseBoolean( str ); }else if( t.equals( String.class ) ){ return str; }else if( t.isEnum() ){ return Enum.valueOf( t, str ); }else if( t.isArray() || t.equals( Vector.class ) ){ // Class tがstatic String getGenericTypeName( String )を実装しているかどうか調べる Method method = null; if( parent_class == null ){ return null; } for( Method m : parent_class.getDeclaredMethods() ){ if( !m.getName().equals( "getGenericTypeName" ) ){ continue; } int modifier = m.getModifiers(); if( !Modifier.isStatic( modifier ) || !Modifier.isPublic( modifier ) ){ continue; } if( !m.getReturnType().equals( String.class ) ){ continue; } Class[] args = m.getParameterTypes(); if( args.length != 1 ){ continue; } if( !args[0].equals( String.class ) ){ continue; } method = m; break; } if( method == null ){ return null; } try{ String content_class_name = (String)method.invoke( null, node.getNodeName() ); Class content_class = Class.forName( content_class_name ); Vector vec = new Vector(); String element_name = getCliTypeName( content_class ); if( element_name.equals( "" ) ){ element_name = content_class.getSimpleName(); } for( int i = 0; i < numChild; i++ ){ Node c = childs.item( i ); if( c.getNodeType() == Node.ELEMENT_NODE ){ Element f = (Element)c; if( !f.getTagName().equals( element_name ) ){ continue; } vec.add( parseNode( content_class, t, c ) ); } } if( t.isArray() ){ int length = vec.size(); Object arr = Array.newInstance( content_class, length ); for( int i = 0; i < length; i++ ){ Array.set( arr, i, vec.get( i ) ); } return arr; }else if( t.equals( Vector.class ) ){ return vec; } }catch( Exception ex ){ System.out.println( "XmlSerializer.parseNode; ex=" + ex ); return null; } } try{ obj = t.newInstance(); }catch( Exception ex ){ return null; } XmlMember[] members = XmlMember.extractMembers( t ); for( int i = 0; i < numChild; i++ ){ Node c = childs.item( i ); if( c.getNodeType() == Node.ELEMENT_NODE ) { Element f = (Element)c; String name = f.getTagName(); for( XmlMember xm : members ){ if( f.getTagName().equals( xm.getName() ) ) { xm.set( obj, parseNode( xm.getType(), t, c ) ); break; } } } } return obj; } public void serialize( OutputStream stream, Object obj ) throws TransformerConfigurationException, ParserConfigurationException, TransformerException, IllegalAccessException{ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); DOMImplementation domImpl=builder.getDOMImplementation(); m_document = domImpl.createDocument( null, m_class.getSimpleName(), null ); Element root = m_document.getDocumentElement(); parseFieldAndProperty( m_class, obj, root ); TransformerFactory tfactory = TransformerFactory.newInstance(); Transformer transformer = tfactory.newTransformer(); if( m_indent ){ transformer.setOutputProperty( OutputKeys.INDENT, "yes" ); } transformer.setOutputProperty( OutputKeys.METHOD, "xml" ); if( m_indent ){ transformer.setOutputProperty( "{http://xml.apache.org/xalan}indent-amount", "" + m_indent_width ); } transformer.transform( new DOMSource( m_document ), new StreamResult( stream ) ); } private void parseFieldAndProperty( Class t, Object obj, Element el ) throws IllegalAccessException{ if( obj == null ){ return; } XmlMember[] members = XmlMember.extractMembers( t ); for( XmlMember xm : members ){ String name = xm.getName(); Element el2 = m_document.createElement( name ); printItemRecurse( xm.getType(), xm.get( obj ), el2 ); el.appendChild( el2 ); } } private void printItemRecurse( Class t, Object obj, Element parent ) throws IllegalAccessException{ try{ if ( !tryWriteValueType( t, obj, parent ) ){ if( t.isArray() || t.equals( Vector.class ) ){ Object[] array = null; if( t.isArray() ){ array = (Object[])obj; }else if( t.equals( Vector.class ) ){ array = ((Vector)obj).toArray(); } if( array != null ){ for( Object o : array ){ if( o != null ){ String name = getCliTypeName( o.getClass() ); if( name.equals( "" ) ){ name = o.getClass().getSimpleName(); } Element element = m_document.createElement( name ); printItemRecurse( o.getClass(), o, element ); parent.appendChild( element ); } } } }else{ parseFieldAndProperty( t, obj, parent ); } } }catch( Exception ex ){ System.out.println( "printItemRecurse; ex=" + ex ); } } private static String getCliTypeName( Class t ){ if( t.equals( Boolean.class ) || t.equals( Boolean.TYPE ) ){ return "bool"; }else if( t.equals( Double.class ) || t.equals( Double.TYPE ) ){ return "double"; }else if( t.equals( Integer.class ) || t.equals( Integer.TYPE ) ){ return "int"; }else if( t.equals( Long.class ) || t.equals( Long.TYPE ) ){ return "long"; }else if( t.equals( Short.class ) || t.equals( Short.TYPE ) ){ return "short"; }else if( t.equals( Float.class ) || t.equals( Float.TYPE ) ){ return "float"; }else if( t.equals( String.class ) ){ return "string"; }else{ return ""; } } private boolean tryWriteValueType( Class t, Object obj, Element element ){ if( t.equals( Boolean.class ) || t.equals( Boolean.TYPE ) ){ element.appendChild( m_document.createTextNode( (Boolean)obj + "" ) ); return true; }else if( t.equals( Double.class ) || t.equals( Double.TYPE ) ){ element.appendChild( m_document.createTextNode( (Double)obj + "" ) ); return true; }else if( t.equals( Integer.class ) || t.equals( Integer.TYPE ) ){ element.appendChild( m_document.createTextNode( (Integer)obj + "" ) ); return true; }else if( t.equals( Long.class ) || t.equals( Long.TYPE ) ){ element.appendChild( m_document.createTextNode( (Long)obj + "" ) ); return true; }else if( t.equals( Short.class ) || t.equals( Short.TYPE ) ){ element.appendChild( m_document.createTextNode( (Short)obj + "" ) ); return true; }else if( t.equals( Float.class ) || t.equals( Float.TYPE ) ){ element.appendChild( m_document.createTextNode( (Float)obj + "" ) ); return true; }else if( t.equals( String.class ) ){ if( obj == null ){ element.appendChild( m_document.createTextNode( "" ) ); }else{ element.appendChild( m_document.createTextNode( (String)obj ) ); } return true; }else if( t.isEnum() ){ if( obj == null ){ for( Field f : t.getDeclaredFields() ){ String name = f.getName(); if( !name.startsWith( "$" ) ){ element.appendChild( m_document.createTextNode( name ) ); break; } } }else{ element.appendChild( m_document.createTextNode( obj + "" ) ); } return true; }else{ return false; } } } #else using System; using System.IO; using bocoree.xml; namespace bocoree.xml { public class XmlSerializer { private bool m_serialize_static_mode = false; System.Xml.Serialization.XmlSerializer m_serializer; XmlStaticMemberSerializer m_static_serializer; public XmlSerializer( Type cls ) { m_serializer = new System.Xml.Serialization.XmlSerializer( cls ); } public XmlSerializer( Type cls, bool serialize_static_mode ) { m_serialize_static_mode = serialize_static_mode; if ( serialize_static_mode ) { m_static_serializer = new XmlStaticMemberSerializer( cls ); } else { m_serializer = new System.Xml.Serialization.XmlSerializer( cls ); } } public object deserialize( Stream stream ) { if ( m_serialize_static_mode ) { m_static_serializer.Deserialize( stream ); return null; } else { return m_serializer.Deserialize( stream ); } } public void serialize( Stream stream, object obj ) { if ( m_serialize_static_mode ) { m_static_serializer.Serialize( stream ); } else { m_serializer.Serialize( stream, obj ); } } } } #endif