我正在通过http请求文件。该文件包含有关其他文件的信息,这些信息需要包含在主文件中。就我而言,这些是带有导入的xsd文件,但我认为可以是任何东西。
您可以在此处查看代码:https://stackblitz.com/edit/angular-ugtaka
我提供了一些控制台输出,这些输出显示将执行每个请求,但最后,我的可观察对象没有发出任何值。
共有3种xsd文件,其结构如下:main.xsd导入sub1.xsd和sub2.xsd,sub2.xsd导入sub1.xsd。
每个文件都应该有一个请求,并且sub1.xsd将被请求两次。
myObservable
.pipe(mergeMap((data) => {
if (data.import !== undefined) {
const requests: Observable<any>[] = [];
if (data.import instanceof Array) {
for (const xmlImport of data.import) {
const localPath = `/assets/${xmlImport.path}`;
requests.push(this.getXsdSchema(localPath));
}
const forked = combineLatest(requests);
return forked;
} else {
const localPath = `/assets/${import.path}`;
return this.getXsdSchema(localPath);
}
}
const myEmpty = never();
return myEmpty;
}))
这不是演示中的实际代码。我尝试将其缩短一些,只包括可能出现问题的部分。
myObservable 是已解析的xsd文件,其中包含应合并到observable的import语句。如果导入次数不止,我尝试将它们与 combineLatest 合并,然后使用 mergeMap 运算符将其合并到主要的可观察对象中。
如果只有一个,则可以跳过 combineLatest 部分,然后直接将其返回;如果没有,则尝试使用 never 和 empty 。< / p>
最后,我想拥有一个包含所有可观察物的流。我想使用reduce运算符将它们组合成一个包含所有文件信息的对象。
如果我将第68行中的 never()替换为 of(“ nothing”)之类的东西,则可观察对象将发出值,但不包括实际信息。
>答案 0 :(得分:2)
问题在于路径为xs_schema.xs_schema.xs_import
。可以通过使用explicitRoot: false
选项来避免这种情况。我为您的stackblitz分叉了,基本上它是替换xs_import
属性中文件的数据:
public getXsdSchema(path: string): Observable<any> {
//console.log(path);
return this.http.get(path, {
responseType: 'text',
headers: new HttpHeaders({
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
})
})
.pipe(concatMap(data => {
return new Promise((resolve, reject) => {
xml2js.parseString(data, {
explicitArray: false,
explicitRoot: false,
tagNameProcessors: [function (name) {
// replace ":" with "_" for all tag names to make sure that they can be accessed within javascript
return name.replace(/:/g, '_');
}]
}, (err, xmlObject) => {
err ? reject(err) : resolve(xmlObject);
});
});
}),
concatMap((data: any) => {
if (data.xs_import) {
if (!Array.isArray(data.xs_import)) {
data.xs_import = data.xs_import ? [data.xs_import] : [];
}
return zip(...data.xs_import.map((xmlImport) =>
this.getXsdSchema(`/assets/${xmlImport.$.schemaLocation}`)
)).pipe(
map((imports) => {
data.xs_import = imports;
return data;
})
);
} else {
return of(data);
}
}))
}
仅供参考:您可以通过在管道调用中用逗号分隔运算符来组合运算符。
结果是:
$: Object
xs_element: Object
xs_import: Array[2]
0: Object
$: Object
xs_simpleType: Object
1: Object
$: Object
xs_complexType: Object
xs_import: Array[1]
0: Object
$: Object
xs_simpleType: Object
答案 1 :(得分:1)
PierreDuc的答案很酷,因为它产生了一个已解析的XML树。我的另一种方法与原始代码相似,不同之处在于,我使用expand进行递归调用,最后将数据缩减为包含四个项目的平面数组。顺便说一句,xml2js.parseString需要包装在Observable中
import { Component } from '@angular/core';
import { HttpHeaders, HttpClient } from '@angular/common/http';
import * as xml2js from 'xml2js';
import { Observable, forkJoin, empty, never, combineLatest, of, merge,zip ,concat} from 'rxjs';
import { switchMap, expand,mergeMap, map, catchError, tap,last ,scan,reduce} from 'rxjs/operators';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular';
constructor(private http: HttpClient) {
this.getXsdSchema('/assets/main.xsd').subscribe((data) => {
console.log('data',data);
});
}
public getXsdSchema(path: string): Observable<any> {
return this.http.get(path, {
responseType: 'text',
headers: new HttpHeaders({
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
})
})
.pipe(
mergeMap(data => {
let rtrn: any;
return new Observable(obs=>{
xml2js.parseString(data, {
explicitArray: false,
tagNameProcessors: [function (name) {
// replace ":" with "_" for all tag names to make sure that they can be accessed within javascript
return name.replace(/:/g, '_');
}]
}, (err, xmlObject) => {
if (err) {
obs.error(err)
} else {
obs.next(xmlObject)
obs.complete()
}
});
})
})
,expand((data)=>{
if (data.xs_schema&&data.xs_schema.xs_import !== undefined) {
const requests: Observable<any>[] = [];
if (data.xs_schema.xs_import instanceof Array) {
for (const xmlImport of data.xs_schema.xs_import) {
const localPath = `/assets/${xmlImport.$.schemaLocation}`;
requests.push(this.getXsdSchema(localPath));
//return this.getXsdSchema(localPath)
}
const forked = merge(...requests);
return forked;
} else {
const localPath = `/assets/${data.xs_schema.xs_import.$.schemaLocation}`;
return this.getXsdSchema(localPath);
}
}
return empty()
})
,reduce((acc,curr)=>acc.concat(curr),[])
)
}
}