lipsync/bocoree/XmlSerializer.cs
2024-05-20 00:17:44 +00:00

432 lines
16 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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シリアライザ<br>
* シリアライズしたいクラスには,以下のメソッドを実装しておく必要があります.<br>
* <dl>
* <dt>getXmlElementNameメソッド</dt>
* <dd>フィールド名やgetter/setter名からXMLのード名を調べる</dd>
* <dt>isXmlIgnoredメソッド</dt>
* <dd>アイテムをXMLに出力するかどうかを決める</dd>
* <dt>getGenericTypeNameメソッド</dt>
* <dd>アイテムが総称型を含むクラスの場合に,その総称型名を調べる</dd>
* </dl>
* <pre>
* public class Test2{
public float value = 1.0f;
private boolean m_b = true;
private int m_i = 2;
public Vector<Integer> list = new Vector<Integer>();
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 "";
}
}
* </pre>
* このように実装しておくとだいたい以下のようなXMLの入出力が可能になります
* <pre>
&lt;Test2&gt;
&lt;Value&gt;1.0&lt;/Value&gt;
&lt;Hoge&gt;true&lt;/Hoge&gt;
&lt;List&gt;
&lt;int&gt;1&lt;/int&gt;
&lt;int&gt;2&lt;/int&gt;
&lt;/List&gt;
&lt;/Test2&gt;
* </pre>
*/
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<Object> vec = new Vector<Object>();
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