1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package net.sf.json.xml;
18
19 import java.io.BufferedReader;
20 import java.io.ByteArrayOutputStream;
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
26 import java.io.OutputStream;
27 import java.io.StringReader;
28 import java.io.UnsupportedEncodingException;
29 import java.util.Arrays;
30 import java.util.Iterator;
31 import java.util.Map;
32 import java.util.TreeMap;
33
34 import net.sf.json.JSON;
35 import net.sf.json.JSONArray;
36 import net.sf.json.JSONException;
37 import net.sf.json.JSONFunction;
38 import net.sf.json.JSONNull;
39 import net.sf.json.JSONObject;
40 import net.sf.json.util.JSONUtils;
41 import nu.xom.Attribute;
42 import nu.xom.Builder;
43 import nu.xom.Document;
44 import nu.xom.Element;
45 import nu.xom.Elements;
46 import nu.xom.Node;
47 import nu.xom.Serializer;
48 import nu.xom.Text;
49
50 import org.apache.commons.lang.ArrayUtils;
51 import org.apache.commons.lang.StringUtils;
52 import org.apache.commons.logging.Log;
53 import org.apache.commons.logging.LogFactory;
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 public class XMLSerializer {
82 private static final String[] EMPTY_ARRAY = new String[0];
83 private static final String JSON_PREFIX = "json_";
84 private static final Log log = LogFactory.getLog( XMLSerializer.class );
85
86
87 private String arrayName;
88
89 private String elementName;
90
91 private String[] expandableProperties;
92 private boolean forceTopLevelObject;
93
94 private boolean namespaceLenient;
95
96 private Map namespacesPerElement = new TreeMap();
97
98 private String objectName;
99
100 private boolean removeNamespacePrefixFromElements;
101
102 private String rootName;
103
104 private Map rootNamespace = new TreeMap();
105
106 private boolean skipNamespaces;
107
108 private boolean skipWhitespace;
109
110 private boolean trimSpaces;
111
112 private boolean typeHintsCompatibility;
113
114 private boolean typeHintsEnabled;
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131 public XMLSerializer() {
132 setObjectName( "o" );
133 setArrayName( "a" );
134 setElementName( "e" );
135 setTypeHintsEnabled( true );
136 setTypeHintsCompatibility( true );
137 setNamespaceLenient( false );
138 setSkipNamespaces( false );
139 setRemoveNamespacePrefixFromElements( false );
140 setTrimSpaces( false );
141 setExpandableProperties( EMPTY_ARRAY );
142 setSkipNamespaces( false );
143 }
144
145
146
147
148
149
150
151 public void addNamespace( String prefix, String uri ) {
152 addNamespace( prefix, uri, null );
153 }
154
155
156
157
158
159
160
161
162
163
164 public void addNamespace( String prefix, String uri, String elementName ) {
165 if( StringUtils.isBlank( uri ) ){
166 return;
167 }
168 if( prefix == null ){
169 prefix = "";
170 }
171 if( StringUtils.isBlank( elementName ) ){
172 rootNamespace.put( prefix.trim(), uri.trim() );
173 }else{
174 Map nameSpaces = (Map) namespacesPerElement.get( elementName );
175 if( nameSpaces == null ){
176 nameSpaces = new TreeMap();
177 namespacesPerElement.put( elementName, nameSpaces );
178 }
179 nameSpaces.put( prefix, uri );
180 }
181 }
182
183
184
185
186 public void clearNamespaces() {
187 rootNamespace.clear();
188 namespacesPerElement.clear();
189 }
190
191
192
193
194
195
196
197
198 public void clearNamespaces( String elementName ) {
199 if( StringUtils.isBlank( elementName ) ){
200 rootNamespace.clear();
201 }else{
202 namespacesPerElement.remove( elementName );
203 }
204 }
205
206
207
208
209 public String getArrayName() {
210 return arrayName;
211 }
212
213
214
215
216 public String getElementName() {
217 return elementName;
218 }
219
220
221
222
223 public String[] getExpandableProperties() {
224 return expandableProperties;
225 }
226
227
228
229
230 public String getObjectName() {
231 return objectName;
232 }
233
234
235
236
237 public String getRootName() {
238 return rootName;
239 }
240
241 public boolean isForceTopLevelObject() {
242 return forceTopLevelObject;
243 }
244
245
246
247
248
249 public boolean isNamespaceLenient() {
250 return namespaceLenient;
251 }
252
253
254
255
256
257 public boolean isRemoveNamespacePrefixFromElements() {
258 return removeNamespacePrefixFromElements;
259 }
260
261
262
263
264
265 public boolean isSkipNamespaces() {
266 return skipNamespaces;
267 }
268
269
270
271
272 public boolean isSkipWhitespace() {
273 return skipWhitespace;
274 }
275
276
277
278
279
280 public boolean isTrimSpaces() {
281 return trimSpaces;
282 }
283
284
285
286
287 public boolean isTypeHintsCompatibility() {
288 return typeHintsCompatibility;
289 }
290
291
292
293
294 public boolean isTypeHintsEnabled() {
295 return typeHintsEnabled;
296 }
297
298
299
300
301
302
303
304
305
306 public JSON read( String xml ) {
307 JSON json = null;
308 try{
309 Document doc = new Builder().build( new StringReader( xml ) );
310 Element root = doc.getRootElement();
311 if( isNullObject( root ) ){
312 return JSONNull.getInstance();
313 }
314 String defaultType = getType( root, JSONTypes.STRING );
315 if( isArray( root, true ) ){
316 json = processArrayElement( root, defaultType );
317 if( forceTopLevelObject ){
318 String key = removeNamespacePrefix( root.getQualifiedName() );
319 json = new JSONObject().element( key, json );
320 }
321 }else{
322 json = processObjectElement( root, defaultType );
323 if( forceTopLevelObject ) {
324 String key = removeNamespacePrefix( root.getQualifiedName() );
325 json = new JSONObject().element( key, json );
326 }
327 }
328 }catch( JSONException jsone ){
329 throw jsone;
330 }catch( Exception e ){
331 throw new JSONException( e );
332 }
333 return json;
334 }
335
336
337
338
339
340
341
342
343
344 public JSON readFromFile( File file ) {
345 if( file == null ){
346 throw new JSONException( "File is null" );
347 }
348 if( !file.canRead() ){
349 throw new JSONException( "Can't read input file" );
350 }
351 if( file.isDirectory() ){
352 throw new JSONException( "File is a directory" );
353 }
354 try{
355 return readFromStream( new FileInputStream( file ) );
356 }catch( IOException ioe ){
357 throw new JSONException( ioe );
358 }
359 }
360
361
362
363
364
365
366
367
368
369 public JSON readFromFile( String path ) {
370 return readFromStream( Thread.currentThread()
371 .getContextClassLoader()
372 .getResourceAsStream( path ) );
373 }
374
375
376
377
378
379
380
381
382
383 public JSON readFromStream( InputStream stream ) {
384 try{
385 StringBuffer xml = new StringBuffer();
386 BufferedReader in = new BufferedReader( new InputStreamReader( stream ) );
387 String line = null;
388 while( (line = in.readLine()) != null ){
389 xml.append( line );
390 }
391 return read( xml.toString() );
392 }catch( IOException ioe ){
393 throw new JSONException( ioe );
394 }
395 }
396
397
398
399
400
401
402 public void removeNamespace( String prefix ) {
403 removeNamespace( prefix, null );
404 }
405
406
407
408
409
410
411
412
413
414 public void removeNamespace( String prefix, String elementName ) {
415 if( prefix == null ){
416 prefix = "";
417 }
418 if( StringUtils.isBlank( elementName ) ){
419 rootNamespace.remove( prefix.trim() );
420 }else{
421 Map nameSpaces = (Map) namespacesPerElement.get( elementName );
422 nameSpaces.remove( prefix );
423 }
424 }
425
426
427
428
429
430 public void setArrayName( String arrayName ) {
431 this.arrayName = StringUtils.isBlank( arrayName ) ? "a" : arrayName;
432 }
433
434
435
436
437
438 public void setElementName( String elementName ) {
439 this.elementName = StringUtils.isBlank( elementName ) ? "e" : elementName;
440 }
441
442
443
444
445 public void setExpandableProperties( String[] expandableProperties ) {
446 this.expandableProperties = expandableProperties == null ? EMPTY_ARRAY : expandableProperties;
447 }
448
449 public void setForceTopLevelObject( boolean forceTopLevelObject ) {
450 this.forceTopLevelObject = forceTopLevelObject;
451 }
452
453
454
455
456
457
458
459
460 public void setNamespace( String prefix, String uri ) {
461 setNamespace( prefix, uri, null );
462 }
463
464
465
466
467
468
469
470
471
472
473 public void setNamespace( String prefix, String uri, String elementName ) {
474 if( StringUtils.isBlank( uri ) ){
475 return;
476 }
477 if( prefix == null ){
478 prefix = "";
479 }
480 if( StringUtils.isBlank( elementName ) ){
481 rootNamespace.clear();
482 rootNamespace.put( prefix.trim(), uri.trim() );
483 }else{
484 Map nameSpaces = (Map) namespacesPerElement.get( elementName );
485 if( nameSpaces == null ){
486 nameSpaces = new TreeMap();
487 namespacesPerElement.put( elementName, nameSpaces );
488 }
489 nameSpaces.clear();
490 nameSpaces.put( prefix, uri );
491 }
492 }
493
494
495
496
497 public void setNamespaceLenient( boolean namespaceLenient ) {
498 this.namespaceLenient = namespaceLenient;
499 }
500
501
502
503
504
505 public void setObjectName( String objectName ) {
506 this.objectName = StringUtils.isBlank( objectName ) ? "o" : objectName;
507 }
508
509
510
511
512
513 public void setRemoveNamespacePrefixFromElements( boolean removeNamespacePrefixFromElements ) {
514 this.removeNamespacePrefixFromElements = removeNamespacePrefixFromElements;
515 }
516
517
518
519
520 public void setRootName( String rootName ) {
521 this.rootName = StringUtils.isBlank( rootName ) ? null : rootName;
522 }
523
524
525
526
527
528 public void setSkipNamespaces( boolean skipNamespaces ) {
529 this.skipNamespaces = skipNamespaces;
530 }
531
532
533
534
535 public void setSkipWhitespace( boolean skipWhitespace ) {
536 this.skipWhitespace = skipWhitespace;
537 }
538
539
540
541
542
543 public void setTrimSpaces( boolean trimSpaces ) {
544 this.trimSpaces = trimSpaces;
545 }
546
547
548
549
550 public void setTypeHintsCompatibility( boolean typeHintsCompatibility ) {
551 this.typeHintsCompatibility = typeHintsCompatibility;
552 }
553
554
555
556
557 public void setTypeHintsEnabled( boolean typeHintsEnabled ) {
558 this.typeHintsEnabled = typeHintsEnabled;
559 }
560
561
562
563
564
565
566
567
568
569 public String write( JSON json ) {
570 return write( json, null );
571 }
572
573
574
575
576
577
578
579
580
581
582
583 public String write( JSON json, String encoding ) {
584 if( JSONNull.getInstance()
585 .equals( json ) ){
586 Element root = null;
587 root = newElement( getRootName() == null ? getObjectName() : getRootName() );
588 root.addAttribute( new Attribute( addJsonPrefix( "null" ), "true" ) );
589 Document doc = new Document( root );
590 return writeDocument( doc, encoding );
591 }else if( json instanceof JSONArray ){
592 JSONArray jsonArray = (JSONArray) json;
593 Element root = processJSONArray( jsonArray,
594 newElement( getRootName() == null ? getArrayName() : getRootName() ),
595 expandableProperties );
596 Document doc = new Document( root );
597 return writeDocument( doc, encoding );
598 }else{
599 JSONObject jsonObject = (JSONObject) json;
600 Element root = null;
601 if( jsonObject.isNullObject() ){
602 root = newElement( getObjectName() );
603 root.addAttribute( new Attribute( addJsonPrefix( "null" ), "true" ) );
604 }else{
605 root = processJSONObject( jsonObject,
606 newElement( getRootName() == null ? getObjectName() : getRootName() ),
607 expandableProperties, true );
608 }
609 Document doc = new Document( root );
610 return writeDocument( doc, encoding );
611 }
612 }
613
614 private String addJsonPrefix( String str ) {
615 if( !isTypeHintsCompatibility() ){
616 return JSON_PREFIX + str;
617 }
618 return str;
619 }
620
621 private void addNameSpaceToElement( Element element ) {
622 String elementName = null;
623 if( element instanceof CustomElement ){
624 elementName = ((CustomElement) element).getQName();
625 }else{
626 elementName = element.getQualifiedName();
627 }
628 Map nameSpaces = (Map) namespacesPerElement.get( elementName );
629 if( nameSpaces != null && !nameSpaces.isEmpty() ){
630 setNamespaceLenient( true );
631 for( Iterator entries = nameSpaces.entrySet()
632 .iterator(); entries.hasNext(); ){
633 Map.Entry entry = (Map.Entry) entries.next();
634 String prefix = (String) entry.getKey();
635 String uri = (String) entry.getValue();
636 if( StringUtils.isBlank( prefix ) ){
637 element.setNamespaceURI( uri );
638 }else{
639 element.addNamespaceDeclaration( prefix, uri );
640 }
641 }
642 }
643 }
644
645 private boolean checkChildElements( Element element, boolean isTopLevel ) {
646 int childCount = element.getChildCount();
647 Elements elements = element.getChildElements();
648 int elementCount = elements.size();
649
650 if( childCount == 1 && element.getChild( 0 ) instanceof Text ){
651 return isTopLevel;
652 }
653
654 if( childCount == elementCount ){
655 if( elementCount == 0 ){
656 return true;
657 }
658 if( elementCount == 1 ){
659 if( skipWhitespace || element.getChild( 0 ) instanceof Text ){
660 return true;
661 }else{
662 return false;
663 }
664 }
665 }
666
667 if( childCount > elementCount ){
668 for( int i = 0; i < childCount; i++ ){
669 Node node = element.getChild( i );
670 if( node instanceof Text ){
671 Text text = (Text) node;
672 if( StringUtils.isNotBlank( StringUtils.strip( text.getValue() ) )
673 && !skipWhitespace ){
674 return false;
675 }
676 }
677 }
678 }
679
680 String childName = elements.get( 0 )
681 .getQualifiedName();
682 for( int i = 1; i < elementCount; i++ ){
683 if( childName.compareTo( elements.get( i )
684 .getQualifiedName() ) != 0 ){
685 return false;
686 }
687 }
688
689 return true;
690 }
691
692 private String getClass( Element element ) {
693 Attribute attribute = element.getAttribute( addJsonPrefix( "class" ) );
694 String clazz = null;
695 if( attribute != null ){
696 String clazzText = attribute.getValue()
697 .trim();
698 if( JSONTypes.OBJECT.compareToIgnoreCase( clazzText ) == 0 ){
699 clazz = JSONTypes.OBJECT;
700 }else if( JSONTypes.ARRAY.compareToIgnoreCase( clazzText ) == 0 ){
701 clazz = JSONTypes.ARRAY;
702 }
703 }
704 return clazz;
705 }
706
707 private String getType( Element element ) {
708 return getType( element, null );
709 }
710
711 private String getType( Element element, String defaultType ) {
712 Attribute attribute = element.getAttribute( addJsonPrefix( "type" ) );
713 String type = null;
714 if( attribute != null ){
715 String typeText = attribute.getValue()
716 .trim();
717 if( JSONTypes.BOOLEAN.compareToIgnoreCase( typeText ) == 0 ){
718 type = JSONTypes.BOOLEAN;
719 }else if( JSONTypes.NUMBER.compareToIgnoreCase( typeText ) == 0 ){
720 type = JSONTypes.NUMBER;
721 }else if( JSONTypes.INTEGER.compareToIgnoreCase( typeText ) == 0 ){
722 type = JSONTypes.INTEGER;
723 }else if( JSONTypes.FLOAT.compareToIgnoreCase( typeText ) == 0 ){
724 type = JSONTypes.FLOAT;
725 }else if( JSONTypes.OBJECT.compareToIgnoreCase( typeText ) == 0 ){
726 type = JSONTypes.OBJECT;
727 }else if( JSONTypes.ARRAY.compareToIgnoreCase( typeText ) == 0 ){
728 type = JSONTypes.ARRAY;
729 }else if( JSONTypes.STRING.compareToIgnoreCase( typeText ) == 0 ){
730 type = JSONTypes.STRING;
731 }else if( JSONTypes.FUNCTION.compareToIgnoreCase( typeText ) == 0 ){
732 type = JSONTypes.FUNCTION;
733 }
734 }else{
735 if( defaultType != null ){
736 log.info( "Using default type " + defaultType );
737 type = defaultType;
738 }
739 }
740 return type;
741 }
742
743 private boolean hasNamespaces( Element element ) {
744 int namespaces = 0;
745 for( int i = 0; i < element.getNamespaceDeclarationCount(); i++ ){
746 String prefix = element.getNamespacePrefix( i );
747 String uri = element.getNamespaceURI( prefix );
748 if( StringUtils.isBlank( uri ) ){
749 continue;
750 }
751 namespaces++;
752 }
753 return namespaces > 0;
754 }
755
756 private boolean isArray( Element element, boolean isTopLevel ) {
757 boolean isArray = false;
758 String clazz = getClass( element );
759 if( clazz != null && clazz.equals( JSONTypes.ARRAY ) ){
760 isArray = true;
761 }else if( element.getAttributeCount() == 0 ){
762 isArray = checkChildElements( element, isTopLevel );
763 }else if( element.getAttributeCount() == 1
764 && (element.getAttribute( addJsonPrefix( "class" ) ) != null || element.getAttribute( addJsonPrefix( "type" ) ) != null) ){
765 isArray = checkChildElements( element, isTopLevel );
766 }else if( element.getAttributeCount() == 2
767 && (element.getAttribute( addJsonPrefix( "class" ) ) != null && element.getAttribute( addJsonPrefix( "type" ) ) != null) ){
768 isArray = checkChildElements( element, isTopLevel );
769 }
770
771 if( isArray ){
772
773 for( int j = 0; j < element.getNamespaceDeclarationCount(); j++ ){
774 String prefix = element.getNamespacePrefix( j );
775 String uri = element.getNamespaceURI( prefix );
776 if( !StringUtils.isBlank( uri ) ){
777 return false;
778 }
779 }
780 }
781
782 return isArray;
783 }
784
785 private boolean isFunction( Element element ) {
786 int attrCount = element.getAttributeCount();
787 if( attrCount > 0 ){
788 Attribute typeAttr = element.getAttribute( addJsonPrefix( "type" ) );
789 Attribute paramsAttr = element.getAttribute( addJsonPrefix( "params" ) );
790 if( attrCount == 1 && paramsAttr != null ){
791 return true;
792 }
793 if( attrCount == 2 && paramsAttr != null && typeAttr != null && (typeAttr.getValue()
794 .compareToIgnoreCase( JSONTypes.STRING ) == 0 || typeAttr.getValue()
795 .compareToIgnoreCase( JSONTypes.FUNCTION ) == 0) ){
796 return true;
797 }
798 }
799 return false;
800 }
801
802 private boolean isNullObject( Element element ) {
803 if( element.getChildCount() == 0 ){
804 if( element.getAttributeCount() == 0 ){
805 return true;
806 }else if( element.getAttribute( addJsonPrefix( "null" ) ) != null ){
807 return true;
808 }else if( element.getAttributeCount() == 1
809 && (element.getAttribute( addJsonPrefix( "class" ) ) != null || element.getAttribute( addJsonPrefix( "type" ) ) != null) ){
810 return true;
811 }else if( element.getAttributeCount() == 2
812 && (element.getAttribute( addJsonPrefix( "class" ) ) != null && element.getAttribute( addJsonPrefix( "type" ) ) != null) ){
813 return true;
814 }
815 }
816 if( skipWhitespace && element.getChildCount() == 1 && element.getChild( 0 ) instanceof Text ){
817 return true;
818 }
819 return false;
820 }
821
822 private boolean isObject( Element element, boolean isTopLevel ) {
823 boolean isObject = false;
824 if( !isArray( element, isTopLevel ) && !isFunction( element ) ){
825 if( hasNamespaces( element ) ){
826 return true;
827 }
828
829 int attributeCount = element.getAttributeCount();
830 if( attributeCount > 0 ){
831 int attrs = element.getAttribute( addJsonPrefix( "null" )) == null ? 0 : 1;
832 attrs += element.getAttribute( addJsonPrefix( "class" )) == null ? 0: 1;
833 attrs += element.getAttribute( addJsonPrefix( "type" ))== null ? 0 : 1;
834 switch( attributeCount ){
835 case 1:
836 if( attrs == 0){
837 return true;
838 }
839 break;
840 case 2:
841 if( attrs < 2 ){
842 return true;
843 }
844 break;
845 case 3:
846 if( attrs < 3 ){
847 return true;
848 }
849 break;
850 default:
851 return true;
852 }
853 }
854
855 int childCount = element.getChildCount();
856 if( childCount == 1 && element.getChild( 0 ) instanceof Text ){
857 return isTopLevel;
858 }
859
860 isObject = true;
861 }
862 return isObject;
863 }
864
865 private Element newElement( String name ) {
866 if( name.indexOf( ':' ) != -1 ){
867 namespaceLenient = true;
868 }
869 return namespaceLenient ? new CustomElement( name ) : new Element( name );
870 }
871
872 private JSON processArrayElement( Element element, String defaultType ) {
873 JSONArray jsonArray = new JSONArray();
874
875 int childCount = element.getChildCount();
876 for( int i = 0; i < childCount; i++ ){
877 Node child = element.getChild( i );
878 if( child instanceof Text ){
879 Text text = (Text) child;
880 if( StringUtils.isNotBlank( StringUtils.strip( text.getValue() ) ) ){
881 jsonArray.element( text.getValue() );
882 }
883 }else if( child instanceof Element ){
884 setValue( jsonArray, (Element) child, defaultType );
885 }
886 }
887 return jsonArray;
888 }
889
890 private Object processElement( Element element, String type ) {
891 if( isNullObject( element ) ){
892 return JSONNull.getInstance();
893 }else if( isArray( element, false ) ){
894 return processArrayElement( element, type );
895 }else if( isObject( element, false ) ){
896 return processObjectElement( element, type );
897 }else{
898 return trimSpaceFromValue( element.getValue() );
899 }
900 }
901
902 private Element processJSONArray( JSONArray array, Element root, String[] expandableProperties ) {
903 int l = array.size();
904 for( int i = 0; i < l; i++ ){
905 Object value = array.get( i );
906 Element element = processJSONValue( value, root, null, expandableProperties );
907 root.appendChild( element );
908 }
909 return root;
910 }
911
912 private Element processJSONObject( JSONObject jsonObject, Element root,
913 String[] expandableProperties, boolean isRoot ) {
914 if( jsonObject.isNullObject() ){
915 root.addAttribute( new Attribute( addJsonPrefix( "null" ), "true" ) );
916 return root;
917 }else if( jsonObject.isEmpty() ){
918 return root;
919 }
920
921 if( isRoot ){
922 if( !rootNamespace.isEmpty() ){
923 setNamespaceLenient( true );
924 for( Iterator entries = rootNamespace.entrySet()
925 .iterator(); entries.hasNext(); ){
926 Map.Entry entry = (Map.Entry) entries.next();
927 String prefix = (String) entry.getKey();
928 String uri = (String) entry.getValue();
929 if( StringUtils.isBlank( prefix ) ){
930 root.setNamespaceURI( uri );
931 }else{
932 root.addNamespaceDeclaration( prefix, uri );
933 }
934 }
935 }
936 }
937
938 addNameSpaceToElement( root );
939
940 Object[] names = jsonObject.names()
941 .toArray();
942 Arrays.sort( names );
943 Element element = null;
944 for( int i = 0; i < names.length; i++ ){
945 String name = (String) names[i];
946 Object value = jsonObject.get( name );
947 if( name.startsWith( "@xmlns" ) ){
948 setNamespaceLenient( true );
949 int colon = name.indexOf( ':' );
950 if( colon == -1 ){
951
952 if( StringUtils.isBlank( root.getNamespaceURI() ) ){
953 root.setNamespaceURI( String.valueOf( value ) );
954 }
955 }else{
956 String prefix = name.substring( colon + 1 );
957 if( StringUtils.isBlank( root.getNamespaceURI( prefix ) ) ){
958 root.addNamespaceDeclaration( prefix, String.valueOf( value ) );
959 }
960 }
961 }else if( name.startsWith( "@" ) ){
962 root.addAttribute( new Attribute( name.substring( 1 ), String.valueOf( value ) ) );
963 }else if( name.equals( "#text" ) ){
964 if( value instanceof JSONArray ){
965 root.appendChild( ((JSONArray) value).join( "", true ) );
966 }else{
967 root.appendChild( String.valueOf( value ) );
968 }
969 }else if( value instanceof JSONArray
970 && (((JSONArray) value).isExpandElements() || ArrayUtils.contains(
971 expandableProperties, name )) ){
972 JSONArray array = (JSONArray) value;
973 int l = array.size();
974 for( int j = 0; j < l; j++ ){
975 Object item = array.get( j );
976 element = newElement( name );
977 if( item instanceof JSONObject ){
978 element = processJSONValue( (JSONObject) item, root, element,
979 expandableProperties );
980 }else if( item instanceof JSONArray ){
981 element = processJSONValue( (JSONArray) item, root, element, expandableProperties );
982 }else{
983 element = processJSONValue( item, root, element, expandableProperties );
984 }
985 addNameSpaceToElement( element );
986 root.appendChild( element );
987 }
988 }else{
989 element = newElement( name );
990 element = processJSONValue( value, root, element, expandableProperties );
991 addNameSpaceToElement( element );
992 root.appendChild( element );
993 }
994 }
995 return root;
996 }
997
998 private Element processJSONValue( Object value, Element root, Element target,
999 String[] expandableProperties ) {
1000 if( target == null ){
1001 target = newElement( getElementName() );
1002 }
1003 if( JSONUtils.isBoolean( value ) ){
1004 if( isTypeHintsEnabled() ){
1005 target.addAttribute( new Attribute( addJsonPrefix( "type" ), JSONTypes.BOOLEAN ) );
1006 }
1007 target.appendChild( value.toString() );
1008 }else if( JSONUtils.isNumber( value ) ){
1009 if( isTypeHintsEnabled() ){
1010 target.addAttribute( new Attribute( addJsonPrefix( "type" ), JSONTypes.NUMBER ) );
1011 }
1012 target.appendChild( value.toString() );
1013 }else if( JSONUtils.isFunction( value ) ){
1014 if( value instanceof String ){
1015 value = JSONFunction.parse( (String) value );
1016 }
1017 JSONFunction func = (JSONFunction) value;
1018 if( isTypeHintsEnabled() ){
1019 target.addAttribute( new Attribute( addJsonPrefix( "type" ), JSONTypes.FUNCTION ) );
1020 }
1021 String params = ArrayUtils.toString( func.getParams() );
1022 params = params.substring( 1 );
1023 params = params.substring( 0, params.length() - 1 );
1024 target.addAttribute( new Attribute( addJsonPrefix( "params" ), params ) );
1025 target.appendChild( new Text( "<![CDATA[" + func.getText() + "]]>" ) );
1026 }else if( JSONUtils.isString( value ) ){
1027 if( isTypeHintsEnabled() ){
1028 target.addAttribute( new Attribute( addJsonPrefix( "type" ), JSONTypes.STRING ) );
1029 }
1030 target.appendChild( value.toString() );
1031 }else if( value instanceof JSONArray ){
1032 if( isTypeHintsEnabled() ){
1033 target.addAttribute( new Attribute( addJsonPrefix( "class" ), JSONTypes.ARRAY ) );
1034 }
1035 target = processJSONArray( (JSONArray) value, target, expandableProperties );
1036 }else if( value instanceof JSONObject ){
1037 if( isTypeHintsEnabled() ){
1038 target.addAttribute( new Attribute( addJsonPrefix( "class" ), JSONTypes.OBJECT ) );
1039 }
1040 target = processJSONObject( (JSONObject) value, target, expandableProperties, false );
1041 }else if( JSONUtils.isNull( value ) ){
1042 if( isTypeHintsEnabled() ){
1043 target.addAttribute( new Attribute( addJsonPrefix( "class" ), JSONTypes.OBJECT ) );
1044 }
1045 target.addAttribute( new Attribute( addJsonPrefix( "null" ), "true" ) );
1046 }
1047 return target;
1048 }
1049
1050 private JSON processObjectElement( Element element, String defaultType ) {
1051 if( isNullObject( element ) ){
1052 return JSONNull.getInstance();
1053 }
1054 JSONObject jsonObject = new JSONObject();
1055
1056 if( !skipNamespaces ){
1057 for( int j = 0; j < element.getNamespaceDeclarationCount(); j++ ){
1058 String prefix = element.getNamespacePrefix( j );
1059 String uri = element.getNamespaceURI( prefix );
1060 if( StringUtils.isBlank( uri ) ){
1061 continue;
1062 }
1063 if( !StringUtils.isBlank( prefix ) ){
1064 prefix = ":" + prefix;
1065 }
1066 setOrAccumulate( jsonObject, "@xmlns" + prefix, trimSpaceFromValue( uri ) );
1067 }
1068 }
1069
1070
1071 int attrCount = element.getAttributeCount();
1072 for( int i = 0; i < attrCount; i++ ){
1073 Attribute attr = element.getAttribute( i );
1074 String attrname = attr.getQualifiedName();
1075 if( isTypeHintsEnabled()
1076 && (addJsonPrefix( "class" ).compareToIgnoreCase( attrname ) == 0 || addJsonPrefix(
1077 "type" ).compareToIgnoreCase( attrname ) == 0) ){
1078 continue;
1079 }
1080 String attrvalue = attr.getValue();
1081 setOrAccumulate( jsonObject, "@" + removeNamespacePrefix( attrname ),
1082 trimSpaceFromValue( attrvalue ) );
1083 }
1084
1085
1086 int childCount = element.getChildCount();
1087 for( int i = 0; i < childCount; i++ ){
1088 Node child = element.getChild( i );
1089 if( child instanceof Text ){
1090 Text text = (Text) child;
1091 if( StringUtils.isNotBlank( StringUtils.strip( text.getValue() ) ) ){
1092 setOrAccumulate( jsonObject, "#text", trimSpaceFromValue( text.getValue() ) );
1093 }
1094 }else if( child instanceof Element ){
1095 setValue( jsonObject, (Element) child, defaultType );
1096 }
1097 }
1098
1099 return jsonObject;
1100 }
1101
1102 private String removeNamespacePrefix( String name ) {
1103 if( isRemoveNamespacePrefixFromElements() ){
1104 int colon = name.indexOf( ':' );
1105 return colon != -1 ? name.substring( colon + 1 ) : name;
1106 }
1107 return name;
1108 }
1109
1110 private void setOrAccumulate( JSONObject jsonObject, String key, Object value ) {
1111 if( jsonObject.has( key ) ){
1112 jsonObject.accumulate( key, value );
1113 Object val = jsonObject.get( key );
1114 if( val instanceof JSONArray ){
1115 ((JSONArray) val).setExpandElements( true );
1116 }
1117 }else{
1118 jsonObject.element( key, value );
1119 }
1120 }
1121
1122 private void setValue( JSONArray jsonArray, Element element, String defaultType ) {
1123 String clazz = getClass( element );
1124 String type = getType( element );
1125 type = (type == null) ? defaultType : type;
1126
1127 if( hasNamespaces( element ) && !skipNamespaces ){
1128 jsonArray.element( simplifyValue( null, processElement( element, type ) ) );
1129 return;
1130 }else if( element.getAttributeCount() > 0 ){
1131 if( isFunction( element ) ){
1132 Attribute paramsAttribute = element.getAttribute( addJsonPrefix( "params" ) );
1133 String[] params = null;
1134 String text = element.getValue();
1135 params = StringUtils.split( paramsAttribute.getValue(), "," );
1136 jsonArray.element( new JSONFunction( params, text ) );
1137 return;
1138 }else{
1139 jsonArray.element( simplifyValue( null, processElement( element, type ) ) );
1140 return;
1141 }
1142 }
1143
1144 boolean classProcessed = false;
1145 if( clazz != null ){
1146 if( clazz.compareToIgnoreCase( JSONTypes.ARRAY ) == 0 ){
1147 jsonArray.element( processArrayElement( element, type ) );
1148 classProcessed = true;
1149 }else if( clazz.compareToIgnoreCase( JSONTypes.OBJECT ) == 0 ){
1150 jsonArray.element( simplifyValue( null, processObjectElement( element, type ) ) );
1151 classProcessed = true;
1152 }
1153 }
1154 if( !classProcessed ){
1155 if( type.compareToIgnoreCase( JSONTypes.BOOLEAN ) == 0 ){
1156 jsonArray.element( Boolean.valueOf( element.getValue() ) );
1157 }else if( type.compareToIgnoreCase( JSONTypes.NUMBER ) == 0 ){
1158
1159 try{
1160 jsonArray.element( Integer.valueOf( element.getValue() ) );
1161 }catch( NumberFormatException e ){
1162 jsonArray.element( Double.valueOf( element.getValue() ) );
1163 }
1164 }else if( type.compareToIgnoreCase( JSONTypes.INTEGER ) == 0 ){
1165 jsonArray.element( Integer.valueOf( element.getValue() ) );
1166 }else if( type.compareToIgnoreCase( JSONTypes.FLOAT ) == 0 ){
1167 jsonArray.element( Double.valueOf( element.getValue() ) );
1168 }else if( type.compareToIgnoreCase( JSONTypes.FUNCTION ) == 0 ){
1169 String[] params = null;
1170 String text = element.getValue();
1171 Attribute paramsAttribute = element.getAttribute( addJsonPrefix( "params" ) );
1172 if( paramsAttribute != null ){
1173 params = StringUtils.split( paramsAttribute.getValue(), "," );
1174 }
1175 jsonArray.element( new JSONFunction( params, text ) );
1176 }else if( type.compareToIgnoreCase( JSONTypes.STRING ) == 0 ){
1177
1178 Attribute paramsAttribute = element.getAttribute( addJsonPrefix( "params" ) );
1179 if( paramsAttribute != null ){
1180 String[] params = null;
1181 String text = element.getValue();
1182 params = StringUtils.split( paramsAttribute.getValue(), "," );
1183 jsonArray.element( new JSONFunction( params, text ) );
1184 }else{
1185 if( isArray( element, false ) ){
1186 jsonArray.element( processArrayElement( element, defaultType ) );
1187 }else if( isObject( element, false ) ){
1188 jsonArray.element( simplifyValue( null, processObjectElement( element,
1189 defaultType ) ) );
1190 }else{
1191 jsonArray.element( trimSpaceFromValue( element.getValue() ) );
1192 }
1193 }
1194 }
1195 }
1196 }
1197
1198 private void setValue( JSONObject jsonObject, Element element, String defaultType ) {
1199 String clazz = getClass( element );
1200 String type = getType( element );
1201 type = (type == null) ? defaultType : type;
1202
1203
1204
1205 String key = removeNamespacePrefix( element.getQualifiedName() );
1206 if( hasNamespaces( element ) && !skipNamespaces ){
1207 setOrAccumulate( jsonObject, key, simplifyValue( jsonObject,
1208 processElement( element, type ) ) );
1209 return;
1210 }else if( element.getAttributeCount() > 0 ){
1211 if( isFunction( element ) ){
1212 Attribute paramsAttribute = element.getAttribute( addJsonPrefix( "params" ) );
1213 String text = element.getValue();
1214 String[] params = StringUtils.split( paramsAttribute.getValue(), "," );
1215 setOrAccumulate( jsonObject, key, new JSONFunction( params, text ) );
1216 return;
1217 }
1218
1219
1220
1221
1222 }
1223
1224 boolean classProcessed = false;
1225 if( clazz != null ){
1226 if( clazz.compareToIgnoreCase( JSONTypes.ARRAY ) == 0 ){
1227 setOrAccumulate( jsonObject, key, processArrayElement( element, type ) );
1228 classProcessed = true;
1229 }else if( clazz.compareToIgnoreCase( JSONTypes.OBJECT ) == 0 ){
1230 setOrAccumulate( jsonObject, key, simplifyValue( jsonObject, processObjectElement(
1231 element, type ) ) );
1232 classProcessed = true;
1233 }
1234 }
1235 if( !classProcessed ){
1236 if( type.compareToIgnoreCase( JSONTypes.BOOLEAN ) == 0 ){
1237 setOrAccumulate( jsonObject, key, Boolean.valueOf( element.getValue() ) );
1238 }else if( type.compareToIgnoreCase( JSONTypes.NUMBER ) == 0 ){
1239
1240 try{
1241 setOrAccumulate( jsonObject, key, Integer.valueOf( element.getValue() ) );
1242 }catch( NumberFormatException e ){
1243 setOrAccumulate( jsonObject, key, Double.valueOf( element.getValue() ) );
1244 }
1245 }else if( type.compareToIgnoreCase( JSONTypes.INTEGER ) == 0 ){
1246 setOrAccumulate( jsonObject, key, Integer.valueOf( element.getValue() ) );
1247 }else if( type.compareToIgnoreCase( JSONTypes.FLOAT ) == 0 ){
1248 setOrAccumulate( jsonObject, key, Double.valueOf( element.getValue() ) );
1249 }else if( type.compareToIgnoreCase( JSONTypes.FUNCTION ) == 0 ){
1250 String[] params = null;
1251 String text = element.getValue();
1252 Attribute paramsAttribute = element.getAttribute( addJsonPrefix( "params" ) );
1253 if( paramsAttribute != null ){
1254 params = StringUtils.split( paramsAttribute.getValue(), "," );
1255 }
1256 setOrAccumulate( jsonObject, key, new JSONFunction( params, text ) );
1257 }else if( type.compareToIgnoreCase( JSONTypes.STRING ) == 0 ){
1258
1259 Attribute paramsAttribute = element.getAttribute( addJsonPrefix( "params" ) );
1260 if( paramsAttribute != null ){
1261 String[] params = null;
1262 String text = element.getValue();
1263 params = StringUtils.split( paramsAttribute.getValue(), "," );
1264 setOrAccumulate( jsonObject, key, new JSONFunction( params, text ) );
1265 }else{
1266 if( isArray( element, false ) ){
1267 setOrAccumulate( jsonObject, key, processArrayElement( element, defaultType ) );
1268 }else if( isObject( element, false ) ){
1269 setOrAccumulate( jsonObject, key, simplifyValue( jsonObject,
1270 processObjectElement( element, defaultType ) ) );
1271 }else{
1272 setOrAccumulate( jsonObject, key, trimSpaceFromValue( element.getValue() ) );
1273 }
1274 }
1275 }
1276 }
1277 }
1278
1279 private Object simplifyValue( JSONObject parent, Object json ) {
1280 if( json instanceof JSONObject ){
1281 JSONObject object = (JSONObject) json;
1282 if( parent != null ){
1283
1284 for( Iterator entries = parent.entrySet()
1285 .iterator(); entries.hasNext(); ){
1286 Map.Entry entry = (Map.Entry) entries.next();
1287 String key = (String) entry.getKey();
1288 Object value = entry.getValue();
1289 if( key.startsWith( "@xmlns" ) && value.equals( object.opt( key ) ) ){
1290 object.remove( key );
1291 }
1292 }
1293 }
1294 if( object.size() == 1 && object.has( "#text" ) ){
1295 return object.get( "#text" );
1296 }
1297 }
1298 return json;
1299 }
1300 private String trimSpaceFromValue( String value ) {
1301 if( isTrimSpaces() ){
1302 return value.trim();
1303 }
1304 return value;
1305 }
1306 private String writeDocument( Document doc, String encoding ) {
1307 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1308 try{
1309 XomSerializer serializer = (encoding == null) ? new XomSerializer( baos )
1310 : new XomSerializer( baos, encoding );
1311 serializer.write( doc );
1312 encoding = serializer.getEncoding();
1313 }catch( IOException ioe ){
1314 throw new JSONException( ioe );
1315 }
1316
1317 String str = null;
1318 try{
1319 str = baos.toString( encoding );
1320 }catch( UnsupportedEncodingException uee ){
1321 throw new JSONException( uee );
1322 }
1323 return str;
1324 }
1325
1326 private static class CustomElement extends Element {
1327 private static String getName( String name ) {
1328 int colon = name.indexOf( ':' );
1329 if( colon != -1 ){
1330 return name.substring( colon + 1 );
1331 }
1332 return name;
1333 }
1334
1335 private static String getPrefix( String name ) {
1336 int colon = name.indexOf( ':' );
1337 if( colon != -1 ){
1338 return name.substring( 0, colon );
1339 }
1340 return "";
1341 }
1342
1343 private String prefix;
1344
1345 public CustomElement( String name ) {
1346 super( CustomElement.getName( name ) );
1347 prefix = CustomElement.getPrefix( name );
1348 }
1349
1350 public final String getQName() {
1351 if( prefix.length() == 0 ){
1352 return getLocalName();
1353 }else{
1354 return prefix + ":" + getLocalName();
1355 }
1356 }
1357 }
1358
1359 private class XomSerializer extends Serializer {
1360 public XomSerializer( OutputStream out ) {
1361 super( out );
1362 }
1363
1364 public XomSerializer( OutputStream out, String encoding ) throws UnsupportedEncodingException {
1365 super( out, encoding );
1366 }
1367
1368 protected void write( Text text ) throws IOException {
1369 String value = text.getValue();
1370 if( value.startsWith( "<![CDATA[" ) && value.endsWith( "]]>" ) ){
1371 value = value.substring( 9 );
1372 value = value.substring( 0, value.length() - 3 );
1373 writeRaw( "<![CDATA[" );
1374 writeRaw( value );
1375 writeRaw( "]]>" );
1376 }else{
1377 super.write( text );
1378 }
1379 }
1380
1381 protected void writeEmptyElementTag( Element element ) throws IOException {
1382 if( element instanceof CustomElement && isNamespaceLenient() ){
1383 writeTagBeginning( (CustomElement) element );
1384 writeRaw( "/>" );
1385 }else{
1386 super.writeEmptyElementTag( element );
1387 }
1388 }
1389
1390 protected void writeEndTag( Element element ) throws IOException {
1391 if( element instanceof CustomElement && isNamespaceLenient() ){
1392 writeRaw( "</" );
1393 writeRaw( ((CustomElement) element).getQName() );
1394 writeRaw( ">" );
1395 }else{
1396 super.writeEndTag( element );
1397 }
1398 }
1399
1400 protected void writeNamespaceDeclaration( String prefix, String uri ) throws IOException {
1401 if( !StringUtils.isBlank( uri ) ){
1402 super.writeNamespaceDeclaration( prefix, uri );
1403 }
1404 }
1405
1406 protected void writeStartTag( Element element ) throws IOException {
1407 if( element instanceof CustomElement && isNamespaceLenient() ){
1408 writeTagBeginning( (CustomElement) element );
1409 writeRaw( ">" );
1410 }else{
1411 super.writeStartTag( element );
1412 }
1413 }
1414
1415 private void writeTagBeginning( CustomElement element ) throws IOException {
1416 writeRaw( "<" );
1417 writeRaw( element.getQName() );
1418 writeAttributes( element );
1419 writeNamespaceDeclarations( element );
1420 }
1421 }
1422 }